diff --git a/.dockerignore b/.dockerignore old mode 100644 new mode 100755 index 614b191c3..306cb0540 --- a/.dockerignore +++ b/.dockerignore @@ -73,4 +73,9 @@ bld/ # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ \ No newline at end of file +!*.[Cc]ache/ + +docker-compose/ +doc +guidlines +tests diff --git a/.dockleignore b/.dockleignore new file mode 100755 index 000000000..6f539e2d0 --- /dev/null +++ b/.dockleignore @@ -0,0 +1,17 @@ +# Copyright 2023 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Checked and no actual secrets found in dockerfile, just some environment variables for compatibility +CIS-DI-0010 + diff --git a/.editorconfig b/.editorconfig index 35a26b081..7e85a00c9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# Copyright 2022 MONAI Consortium +# Copyright 2022-2023 MONAI Consortium # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ root = true # Copyright File Header -file_header_template = SPDX-FileCopyrightText: © [year file created] - [last year file modified], MONAI Consortium\nSPDX-License-Identifier: Apache License 2.0 +file_header_template = SPDX-FileCopyrightText: © [year file created] - [last year file modified], MONAI Consortium\nSPDX-License-Identifier: Apache License 2.0 dotnet_diagnostic.IDE0073.severity = error # Default settings: @@ -309,3 +309,7 @@ max_line_length = 88 [*.json] indent_size = 2 insert_final_newline = ignore + + +# Spelling +spelling_error_severity = information diff --git a/.github/.gitversion.yml b/.github/.gitversion.yml old mode 100644 new mode 100755 index f50aeb01f..63f328041 --- a/.github/.gitversion.yml +++ b/.github/.gitversion.yml @@ -12,19 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +workflow: GitHubFlow/v1 assembly-versioning-scheme: MajorMinorPatchTag +assembly-file-versioning-scheme: MajorMinorPatchTag mode: ContinuousDelivery branches: main: - tag: '' + label: '' + regex: ^main$ release: - tag: rc + label: rc + regex: ^releases?[/-](?.+) develop: - tag: beta + label: beta + increment: Patch + regex: ^develop$ feature: - tag: alpha.{BranchName} + label: alpha.{BranchName} + regex: ^features?[/-](?.+) pull-request: - tag: pr + label: pr + increment: Patch + regex: ^(pull|pull\-requests|pr)[/-] ignore: sha: [] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml old mode 100644 new mode 100755 index c7885b964..e2dd1f66c --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -# Copyright 2021-2022 MONAI Consortium +# Copyright 2021-2023 MONAI Consortium # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,28 +37,44 @@ jobs: outputs: semVer: ${{ steps.gitversion.outputs.semVer }} - preReleaseLabel: ${{ steps.gitversion.outputs.preReleaseLabel }} - majorMinorPatch: ${{ steps.gitversion.outputs.majorMinorPatch }} + preReleaseLabel: ${{ steps.gitversion.outputs.PreReleaseLabel }} + majorMinorPatch: ${{ steps.gitversion.outputs.MajorMinorPatch }} + nuGetVersionV2: ${{ steps.gitversion.outputs.MajorMinorPatch }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - - name: Install GitVersion - run: dotnet tool install --global GitVersion.Tool + - name: Setup GitVersion + uses: gittools/actions/gitversion/setup@v3.1.11 + with: + versionSpec: '6.0.x' - name: Determine Version id: gitversion - uses: gittools/actions/gitversion/execute@v0.9.15 + uses: gittools/actions/gitversion/execute@v3.1.11 with: useConfigFile: true + updateAssemblyInfo: true + updateAssemblyInfoFilename: src/AssemblyInfo.cs configFilePath: .github/.gitversion.yml + - name: Print AssemblyInfo + run: cat src/AssemblyInfo.cs + + - name: Upload AssemblyInfo + uses: actions/upload-artifact@v4.6.2 + if: always() + with: + name: assembly-info + path: src/AssemblyInfo.cs + retention-days: 30 + CodeQL-Analyze: runs-on: ubuntu-latest permissions: @@ -68,16 +84,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -85,7 +101,7 @@ jobs: ${{ runner.os }}-nuget - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: csharp @@ -98,7 +114,7 @@ jobs: working-directory: ./src - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 analyze: runs-on: ubuntu-latest @@ -109,25 +125,25 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable Homebrew run: echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH - name: Install License Finder tool with Homebrew - uses: tecoli-com/actions-use-homebrew-tools@v1.1 + uses: tecoli-com/actions-use-homebrew-tools@v1.2 with: tools: licensefinder cache: yes - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -160,26 +176,34 @@ jobs: unit-test: runs-on: ubuntu-latest + services: + mongo: + image: mongo + env: + MONGO_INITDB_ROOT_USERNAME: root + MONGO_INITDB_ROOT_PASSWORD: rootpassword + ports: + - 27017:27017 steps: - - name: Set up JDK 11 - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: distribution: zulu - java-version: '11' + java-version: '17' - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | ${{ runner.os }}-nuget - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -209,7 +233,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: find ~+ -type f -name "*.Test.csproj" | xargs -L1 dotnet test -c ${{ env.BUILD_CONFIG }} -v=minimal -r "${{ env.TEST_RESULTS }}" --collect:"XPlat Code Coverage" --settings coverlet.runsettings + run: find ~+ -type f -name "*.Test.csproj" | xargs -L1 dotnet test -c ${{ env.BUILD_CONFIG }} -v=minimal --results-directory "${{ env.TEST_RESULTS }}" --collect:"XPlat Code Coverage" --settings coverlet.runsettings working-directory: ./src - name: End SonarScanner @@ -222,7 +246,7 @@ jobs: - uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - directory: "src/${{ env.TEST_RESULTS }}" + directory: "src/" files: "**/coverage.opencover.xml" flags: unittests name: codecov-umbrella @@ -234,7 +258,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - feature: [AcrApi, DicomDimseScp, DicomDimseScu, DicomWebExport, DicomWebStow, HealthLevel7, Fhir] + feature: [AcrApi, DicomDimseScp, DicomDimseScu, DicomWebExport, DicomWebStow, HealthLevel7, Fhir, RemoteAppExecutionPlugIn] database: [ef, mongodb] fail-fast: false env: @@ -242,16 +266,16 @@ jobs: DOTNET_TEST: ${{ matrix.database }} steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -273,10 +297,10 @@ jobs: popd - name: Upload Integration Test Results - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4.6.2 if: always() with: - name: integration-${{ matrix.feature }} + name: integration-${{ matrix.feature }}-${{ matrix.database }} path: | ${{ github.workspace }}/LivingDoc.html ${{ github.workspace }}/services.log @@ -287,12 +311,13 @@ jobs: runs-on: ${{ matrix.os }} needs: [calc-version] env: + NUGETVER: ${{ needs.calc-version.outputs.nuGetVersionV2 }} SEMVER: ${{ needs.calc-version.outputs.semVer }} - PRERELEASELABEL: ${{ needs.calc-version.outputs.preReleaseLabel }} - MAJORMINORPATCH: ${{ needs.calc-version.outputs.majorMinorPatch }} + PRERELEASELABEL: ${{ needs.calc-version.outputs.PreReleaseLabel }} + MAJORMINORPATCH: ${{ needs.calc-version.outputs.MajorMinorPatch }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] fail-fast: true outputs: @@ -306,22 +331,32 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} restore-keys: | ${{ runner.os }}-nuget + - name: Download AssemblyInfo.cs + id: download + uses: actions/download-artifact@v4 + with: + name: assembly-info + path: src/ + + - name: Print AssemblyInfo + run: cat src/AssemblyInfo.cs + - name: Restore dependencies run: dotnet restore working-directory: ./src @@ -351,14 +386,30 @@ jobs: dir -r ~/release - name: Upload CLI - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4.6.2 with: - name: artifacts + name: artifacts-cli path: ~/release retention-days: 7 + - name: Package API + if: ${{ (matrix.os == 'ubuntu-latest') }} + run: | + mkdir ~/nupkg + dotnet pack -c ${{ env.BUILD_CONFIG }} -o ~/nupkg -p:PackageVersion=${{ env.NUGETVER }} + ls -lR ~/nupkg + working-directory: ./src/Api + + - name: Upload Nuget + if: ${{ matrix.os == 'ubuntu-latest' }} + uses: actions/upload-artifact@v4.6.2 + with: + name: nuget + path: ~/nupkg + retention-days: 30 + - name: Log in to the Container registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v2.2.0 if: ${{ (matrix.os == 'ubuntu-latest') }} with: registry: ${{ env.REGISTRY }} @@ -367,7 +418,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v4.6.0 if: ${{ (matrix.os == 'ubuntu-latest') }} with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} @@ -376,7 +427,7 @@ jobs: type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} - name: Build and push Docker image - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v4.1.1 if: ${{ (matrix.os == 'ubuntu-latest') }} with: context: . @@ -384,24 +435,37 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - - name: Scan image with Azure Container Scan - env: - TRIVY_TIMEOUT_SEC: 360s - uses: Azure/container-scan@v0.1 + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master if: ${{ (matrix.os == 'ubuntu-latest') }} with: - image-name: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} + image-ref: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL' + + - name: Run dockle scan + id: dockle-scan + uses: goodwithtech/dockle-action@main + if: ${{ (matrix.os == 'ubuntu-latest') }} + with: + image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} + format: 'list' + exit-code: '1' + exit-level: 'warn' - name: Anchore container scan id: anchore-scan - uses: anchore/scan-action@v3.3.1 + uses: anchore/scan-action@v3.3.6 if: ${{ (matrix.os == 'ubuntu-latest') }} with: image: ${{ fromJSON(steps.meta.outputs.json).tags[0] }} fail-build: true severity-cutoff: critical - - name: Upload Anchore scan SARIF report + - name: Upload scan SARIF report uses: github/codeql-action/upload-sarif@v2 if: ${{ (matrix.os == 'ubuntu-latest') }} with: @@ -417,18 +481,18 @@ jobs: runs-on: windows-latest needs: [calc-version] env: - SEMVER: ${{ needs.calc-version.outputs.semVer }} + SEMVER: ${{ needs.calc-version.outputs.SemVer }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v3 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: "6.0.x" + dotnet-version: "8.0.x" - name: Enable NuGet cache - uses: actions/cache@v3.0.11 + uses: actions/cache@v4.2.3 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} @@ -436,7 +500,7 @@ jobs: ${{ runner.os }}-nuget - name: Setup DocFX - uses: crazy-max/ghaction-chocolatey@v2 + uses: crazy-max/ghaction-chocolatey@v3 with: args: install docfx @@ -465,32 +529,73 @@ jobs: Get-ChildItem ~\release -Recurse - name: Upload docs - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4.6.2 with: - name: artifacts + name: artifacts-docs path: ~/release retention-days: 7 + + publish: + name: Publish to GitHub Packages + runs-on: ubuntu-latest + needs: [build, unit-test, integration-test] + if: ${{ ! ( github.event.inputs.nuget ) && ! ( contains(github.ref, 'refs/heads/main') ) }} + steps: + - uses: actions/download-artifact@v4 + id: download + + - name: List artifacts + run: ls -ldR ${{steps.download.outputs.download-path}}/**/* + + - uses: actions/setup-dotnet@v4 + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + with: + dotnet-version: "8.0.x" + source-url: https://nuget.pkg.github.com/Project-MONAI/index.json + + - name: Install grp + run: dotnet tool install gpr -g + + - name: Publish to GitHub + run: gpr push '${{ steps.download.outputs.download-path }}/nuget/*.nupkg' --repository ${{ github.repository }} -k ${{ secrets.GITHUB_TOKEN }} release: if: ${{ contains(github.ref, 'refs/heads/main') || contains(github.ref, 'refs/heads/develop') ||contains(github.head_ref, 'release/') || contains(github.head_ref, 'feature/') || contains(github.head_ref, 'develop') }} runs-on: ubuntu-latest needs: [calc-version, unit-test, docs, integration-test, analyze] env: - SEMVER: ${{ needs.calc-version.outputs.semVer }} - PRERELEASELABEL: ${{ needs.calc-version.outputs.preReleaseLabel }} - MAJORMINORPATCH: ${{ needs.calc-version.outputs.majorMinorPatch }} + SEMVER: ${{ needs.calc-version.outputs.SemVer }} + PRERELEASELABEL: ${{ needs.calc-version.outputs.PreReleaseLabel }} + MAJORMINORPATCH: ${{ needs.calc-version.outputs.MajorMinorPatch }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 id: download - name: List artifacts run: ls -ldR ${{steps.download.outputs.download-path}}/**/* + - uses: actions/setup-dotnet@v4 + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + with: + dotnet-version: "8.0.x" + source-url: https://nuget.pkg.github.com/Project-MONAI/index.json + + - name: Install grp + run: dotnet tool install gpr -g + + - name: Publish to GitHub + run: gpr push '${{ steps.download.outputs.download-path }}/nuget/*.nupkg' --repository ${{ github.repository }} -k ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to NuGet.org + run: dotnet nuget push ${{ steps.download.outputs.download-path }}/nuget/*.nupkg -s https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET }} --skip-duplicate + - name: Extract owner and repo uses: jungwinter/split@v2 id: repo @@ -498,13 +603,28 @@ jobs: separator: "/" msg: ${{ github.repository }} + - name: Unzip docs + if: ${{ contains(github.ref, 'refs/heads/main') }} + run: | + mkdir userguide + unzip artifacts-docs/mig-docs-${{ env.SEMVER }}.zip -d userguide/ + ls -lR userguide/ + + - name: Deploy Docs + uses: peaceiris/actions-gh-pages@v3 + if: ${{ contains(github.ref, 'refs/heads/main') }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: userguide/ + publish_branch: docs + - name: Install GitReleaseManager - uses: gittools/actions/gitreleasemanager/setup@v0.9.15 + uses: gittools/actions/gitreleasemanager/setup@v3.1.11 with: - versionSpec: "0.13.x" + versionSpec: '0.18.x' - name: Create release with GitReleaseManager - uses: gittools/actions/gitreleasemanager/create@v0.9.15 + uses: gittools/actions/gitreleasemanager/create@v3.1.11 with: token: ${{ secrets.GITHUB_TOKEN }} owner: ${{ steps.repo.outputs._0 }} @@ -512,21 +632,20 @@ jobs: milestone: ${{ env.MAJORMINORPATCH }} name: "Release v${{ env.MAJORMINORPATCH }}" assets: | - artifacts/mig-cli-${{ env.SEMVER }}-linux-x64.zip - artifacts/mig-cli-${{ env.SEMVER }}-win-x64.zip - artifacts/mig-docs-${{ env.SEMVER }}.zip + artifacts-cli/mig-cli-${{ env.SEMVER }}-linux-x64.zip + artifacts-docs/mig-docs-${{ env.SEMVER }}.zip - name: Publish release with GitReleaseManager - uses: gittools/actions/gitreleasemanager/publish@v0.9.15 + uses: gittools/actions/gitreleasemanager/publish@v3.1.11 if: ${{ contains(github.ref, 'refs/heads/main') }} with: token: ${{ secrets.GITHUB_TOKEN }} owner: ${{ steps.repo.outputs._0 }} repository: ${{ steps.repo.outputs._1 }} - tagName: ${{ env.MAJORMINORPATCH }} + milestone: ${{ env.MAJORMINORPATCH }} - name: Close release with GitReleaseManager - uses: gittools/actions/gitreleasemanager/close@v0.9.15 + uses: gittools/actions/gitreleasemanager/close@v3.1.11 if: ${{ contains(github.ref, 'refs/heads/main') }} with: token: ${{ secrets.GITHUB_TOKEN }} @@ -538,7 +657,7 @@ jobs: if: ${{ contains(github.ref, 'refs/heads/main') }} run: | mkdir userguide - unzip artifacts/mig-docs-${{ env.SEMVER }}.zip -d userguide/ + unzip artifacts-docs/mig-docs-${{ env.SEMVER }}.zip -d userguide/ ls -lR userguide/ - name: Deploy Docs diff --git a/.github/workflows/package-cleanup.yml b/.github/workflows/package-cleanup.yml new file mode 100644 index 000000000..9c125cd28 --- /dev/null +++ b/.github/workflows/package-cleanup.yml @@ -0,0 +1,58 @@ +# Copyright 2021-2023 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + +name: Cleanup Pre-release Packages + +on: + # schedule: + # - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + nuget-api-prerelease-cleanup: + name: pre-release Monai.Deploy.InformaticsGateway.Api + runs-on: ubuntu-latest + steps: + - uses: actions/delete-package-versions@v4 + with: + package-name: 'Monai.Deploy.InformaticsGateway.Api' + package-type: nuget + min-versions-to-keep: 10 + delete-only-pre-release-versions: "true" + + container-prerelease-cleanup: + name: delete pre-release monai-deploy-informatics-gateway + runs-on: ubuntu-latest + steps: + - uses: actions/delete-package-versions@v4 + with: + package-name: 'monai-deploy-informatics-gateway' + package-type: container + min-versions-to-keep: 10 + delete-only-pre-release-versions: "true" + ignore-versions: '^0\.[\d]+\.[\d]+$' + + container-untagged-cleanup: + name: delete untagged monai-deploy-informatics-gateway + runs-on: ubuntu-latest + steps: + - uses: actions/delete-package-versions@v4 + with: + package-name: 'monai-deploy-informatics-gateway' + package-type: container + min-versions-to-keep: 0 + delete-only-untagged-versions: "true" + diff --git a/.gitignore b/.gitignore index 8c4eb593b..990b81827 100644 --- a/.gitignore +++ b/.gitignore @@ -560,3 +560,4 @@ FodyWeavers.xsd # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/aspnetcore,dotnetcore,visualstudio,visualstudiocode +/src/InformaticsGateway/Properties/launchSettings.json diff --git a/.licenserc.yaml b/.licenserc.yaml index f7d23c3d8..6d7eafe28 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -16,7 +16,8 @@ header: license: spdx-id: Apache-2.0 copyright-owner: MONAI Consortium - + copyright-year: 2022-2025 + paths-ignore: - 'LICENSE' - '.github/**/*.md' @@ -31,6 +32,7 @@ header: - 'src/coverlet.runsettings' - 'src/Monai.Deploy.InformaticsGateway.sln' - 'src/Database/EntityFramework/Migrations/**' + - 'src/Plug-ins/RemoteAppExecution/Migrations/**' - 'demos/**/.env/**' - 'demos/**/*.txt' - 'doc/dependency_decisions.yml' @@ -39,6 +41,7 @@ header: - 'tests/Integration.Test/*.dev' - 'tests/Integration.Test/data/**' - 'guidelines/diagrams/*.txt' + - 'src/DicomWebClient/Third-party/**' comment: on-failure diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 000000000..3626c2622 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,19 @@ +# Copyright 2023 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# https://github.com/Project-MONAI/monai-deploy-informatics-gateway/issues/450 +CVE-2018-8292 + +# https://github.com/Project-MONAI/monai-deploy-informatics-gateway/issues/451 +CVE-2019-0820 diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index cb8226e31..11779bb29 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM mcr.microsoft.com/dotnet/sdk:6.0-jammy as build +FROM mcr.microsoft.com/dotnet/sdk:8.0-jammy AS build # Install the tools RUN dotnet tool install --tool-path /tools dotnet-trace @@ -26,7 +26,8 @@ RUN echo "Building MONAI Deploy Informatics Gateway..." RUN dotnet publish -c Release -o out --nologo src/InformaticsGateway/Monai.Deploy.InformaticsGateway.csproj # Build runtime image -FROM mcr.microsoft.com/dotnet/aspnet:6.0-jammy +FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy +RUN adduser --system --group --no-create-home appuser # Enable elastic client compatibility mode ENV ELASTIC_CLIENT_APIVERSIONING=true @@ -34,17 +35,21 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get clean \ && apt-get update \ - && apt-get install -y --no-install-recommends \ - curl \ - && rm -rf /var/lib/apt/lists + && apt-get install -y --no-install-recommends curl \ + && apt-get install -y libc6-dev=2.35-0ubuntu3.* \ + && rm -rf /var/lib/apt/lists # this is a workaround for Mongo encryption library WORKDIR /opt/monai/ig +RUN chown -R appuser:appuser /opt/monai/ig + COPY --from=build /app/out . COPY --from=build /tools /opt/dotnetcore-tools COPY LICENSE ./ COPY docs/compliance/third-party-licenses.md ./ +RUN ln -s /usr/lib/x86_64-linux-gnu/libdl.so.2 /opt/monai/ig/libdl.so # part 2 of workaround for Mongo encryption library + EXPOSE 104 EXPOSE 2575 EXPOSE 5000 @@ -54,4 +59,6 @@ HEALTHCHECK --interval=10s --retries=10 CMD curl --fail http://localhost:5000/he RUN ls -lR /opt/monai/ig ENV PATH="/opt/dotnetcore-tools:${PATH}" +USER appuser + ENTRYPOINT ["/opt/monai/ig/Monai.Deploy.InformaticsGateway"] diff --git a/doc/dependency_decisions.yml b/doc/dependency_decisions.yml old mode 100644 new mode 100755 index dba118184..87f2acc5b --- a/doc/dependency_decisions.yml +++ b/doc/dependency_decisions.yml @@ -1,2458 +1,1781 @@ --- - - :approve - AWSSDK.Core - - :who: mocsharp + - :versions: + - 3.7.402.25 + :when: 2022-08-29T18:11:12.923Z + :who: mocsharp :why: Apache-2.0 (https://github.com/aws/aws-sdk-net/raw/master/License.txt) - :versions: - - 3.7.100.6 - :when: 2022-09-01 23:05:22.203089302 Z - - :approve - AWSSDK.S3 - - :who: mocsharp + - :versions: + - 3.7.305.4 + :when: 2022-08-29T18:11:13.354Z + :who: mocsharp :why: Apache-2.0 (https://github.com/aws/aws-sdk-net/raw/master/License.txt) - :versions: - - 3.7.9.16 - :when: 2022-08-16 23:05:28.458938645 Z - - :approve - AWSSDK.SecurityToken - - :who: mocsharp + - :versions: + - 3.7.401.68 + :when: 2022-08-16T18:11:13.781Z + :who: mocsharp :why: Apache-2.0 (https://github.com/aws/aws-sdk-net/raw/master/License.txt) - :versions: - - 3.7.100.6 - :when: 2022-09-01 23:05:28.920368903 Z +- - :approve + - Ardalis.GuardClauses + - :versions: + - 4.6.0 + :when: 2022-08-16T21:39:30.077Z + :who: mocsharp + :why: MIT (https://github.com/ardalis/GuardClauses.Analyzers/raw/master/LICENSE) +- - :approve + - AspNetCore.HealthChecks.MongoDb + - :versions: + - 8.1.0 + :when: 2022-12-08T23:37:56.206Z + :who: mocsharp + :why: Apache-2.0 (https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/raw/master/LICENSE) - - :approve - BoDi - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/SpecFlowOSS/BoDi/master/LICENSE.txt) - :versions: - - 1.5.0 - :when: 2022-08-16 23:05:29.793349995 Z + - :versions: + - 1.5.0 + :when: 2022-08-16T23:05:29.793Z + :who: mocsharp + :why: Apache-2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt) - - :approve - Castle.Core - - :who: mocsharp + - :versions: + - 5.1.1 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio :why: Apache-2.0 (https://github.com/castleproject/Core/raw/master/LICENSE) - :versions: - - 5.0.0 - :when: 2022-08-16 23:05:30.223218630 Z - - :approve - - Castle.Core - - :who: mocsharp - :why: Apache-2.0 (https://github.com/castleproject/Core/raw/master/LICENSE) - :versions: - - 5.1.0 - :when: 2022-08-16 23:05:30.666349504 Z + - CommunityToolkit.HighPerformance + - :versions: + - 8.3.2 + :when: 2023-08-04T00:02:30.206Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/CommunityToolkit/dotnet/main/License.md) - - :approve - ConsoleAppFramework - - :who: mocsharp + - :versions: + - 4.2.4 + :when: 2022-08-16T23:05:31.110Z + :who: mocsharp :why: MIT (https://github.com/Cysharp/ConsoleAppFramework/raw/master/LICENSE) - :versions: - - 4.2.4 - :when: 2022-08-16 23:05:31.110052610 Z - - :approve - Crayon - - :who: mocsharp + - :versions: + - 2.0.69 + :when: 2022-08-16T23:05:31.541Z + :who: mocsharp :why: MIT (https://github.com/riezebosch/crayon/raw/master/LICENSE) - :versions: - - 2.0.69 - :when: 2022-08-16 23:05:31.541983092 Z - - :approve - - Crc32.NET - - :who: mocsharp - :why: MIT (https://github.com/force-net/Crc32.NET/raw/develop/LICENSE) - :versions: - - 1.2.0 - :when: 2022-08-16 23:05:31.982398334 Z + - DnsClient + - :versions: + - 1.6.1 + :when: 2022-11-16T23:33:33.315Z + :who: mocsharp + :why: Apache-2.0 (https://github.com/MichaCo/DnsClient.NET/raw/dev/LICENSE) - - :approve - Docker.DotNet - - :who: mocsharp + - :versions: + - 3.125.15 + :when: 2022-08-16T23:05:32.422Z + :who: mocsharp :why: MIT (https://github.com/dotnet/Docker.DotNet/raw/master/LICENSE) - :versions: - - 3.125.12 - :when: 2022-08-16 23:05:32.422217566 Z - - :approve - DotNext - - :who: mocsharp + - :versions: + - 5.3.1 + :when: 2022-09-01T23:05:32.857Z + :who: mocsharp :why: MIT (https://github.com/dotnet/dotNext/raw/master/LICENSE) - :versions: - - 4.7.4 - :when: 2022-09-01 23:05:32.857032968 Z - - :approve - DotNext.Threading - - :who: mocsharp + - :versions: + - 5.5.0 + :when: 2022-09-01T23:05:33.298Z + :who: mocsharp :why: MIT (https://github.com/dotnet/dotNext/raw/master/LICENSE) - :versions: - - 4.7.4 - :when: 2022-09-01 23:05:33.298402277 Z - - :approve - FluentAssertions - - :who: mocsharp - :why: Apache-2.0 (https://github.com/fluentassertions/fluentassertions/raw/develop/LICENSE) - :versions: - - 6.8.0 - :when: 2022-08-16 23:05:33.753437127 Z + - :versions: + - 6.12.2 + :when: 2022-08-16T23:05:33.753Z + :who: mocsharp + :why: Apache-2.0 (https://raw.githubusercontent.com/fluentassertions/fluentassertions/refs/tags/6.12.2/LICENSE) - - :approve - Gherkin - - :who: mocsharp - :why: MIT (https://github.com/cucumber/common/raw/main/LICENSE) - :versions: - - 19.0.3 - :when: 2022-08-16 23:05:34.184272525 Z -- - :approve - - GitVersion.MsBuild - - :who: mocsharp - :why: MIT (https://github.com/GitTools/GitVersion/raw/main/LICENSE) - :versions: - - 5.11.1 - :when: 2022-08-16 23:05:34.633372053 Z + - :versions: + - 19.0.3 + :when: 2022-08-16T23:05:34.184Z + :who: mocsharp + :why: MIT (https://github.com/cucumber/gherkin/raw/main/LICENSE) - - :approve - HL7-dotnetcore - - :who: mocsharp + - :versions: + - 2.39.1 + :when: 2022-08-16T23:05:35.066Z + :who: mocsharp :why: MIT (https://github.com/Efferent-Health/HL7-dotnetcore/raw/master/LICENSE.txt) - :versions: - - 2.29.0 - :when: 2022-08-16 23:05:35.066879864 Z - - :approve - Humanizer.Core - - :who: mocsharp - :why: MIT (https://github.com/Humanizr/Humanizer/raw/main/LICENSE) - :versions: - - 2.8.26 - :when: 2022-08-16 23:05:35.500302268 Z -- - :approve - - JetBrains.Annotations - - :who: mocsharp - :why: MIT (https://github.com/JetBrains/JetBrains.Annotations/raw/main/license.md) - :versions: - - 2021.3.0 - :when: 2022-08-16 23:05:35.941494226 Z -- - :approve - - Karambolo.Extensions.Logging.File - - :who: mocsharp - :why: MIT (https://github.com/adams85/filelogger/raw/master/LICENSE) - :versions: - - 3.3.1 - :when: 2022-08-16 23:05:36.373717252 Z + - :versions: + - 2.14.1 + :when: 2022-08-16T23:05:35.500Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/Humanizr/Humanizer/v2.14.1/LICENSE) - - :approve - Macross.Json.Extensions - - :who: mocsharp + - :versions: + - 3.0.0 + :when: 2022-08-16T23:05:36.807Z + :who: mocsharp :why: MIT (https://github.com/Macross-Software/core/raw/develop/LICENSE.txt) - :versions: - - 3.0.0 - :when: 2022-08-16 23:05:36.807242114 Z -- - :approve - - Microsoft.AspNet.WebApi.Client - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 5.2.9 - :when: 2022-08-16 23:05:37.244143533 Z -- - :approve - - Microsoft.AspNetCore.Authentication.Abstractions - - :who: mocsharp - :why: Apache-2.0 (https://github.com/aspnet/HttpAbstractions/raw/master/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:37.692190919 Z -- - :approve - - Microsoft.AspNetCore.Authentication.Core - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:38.127401143 Z -- - :approve - - Microsoft.AspNetCore.Authorization - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:38.584642464 Z - - :approve - - Microsoft.AspNetCore.Authorization.Policy - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:39.080479670 Z -- - :approve - - Microsoft.AspNetCore.Hosting.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:39.515329423 Z -- - :approve - - Microsoft.AspNetCore.Hosting.Server.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:39.956722759 Z -- - :approve - - Microsoft.AspNetCore.Http - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:40.388929669 Z -- - :approve - - Microsoft.AspNetCore.Http.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:40.826627099 Z -- - :approve - - Microsoft.AspNetCore.Http.Extensions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:41.270544665 Z -- - :approve - - Microsoft.AspNetCore.Http.Features - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:41.699752560 Z -- - :approve - - Microsoft.AspNetCore.JsonPatch - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:42.149729514 Z -- - :approve - - Microsoft.AspNetCore.Mvc.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:42.597684214 Z -- - :approve - - Microsoft.AspNetCore.Mvc.Core - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:43.039048876 Z -- - :approve - - Microsoft.AspNetCore.Mvc.Formatters.Json - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:43.483158620 Z -- - :approve - - Microsoft.AspNetCore.Mvc.WebApiCompatShim - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:43.907848139 Z -- - :approve - - Microsoft.AspNetCore.ResponseCaching.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:44.369404969 Z -- - :approve - - Microsoft.AspNetCore.Routing - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:44.808937623 Z -- - :approve - - Microsoft.AspNetCore.Routing.Abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:45.246897771 Z -- - :approve - - Microsoft.AspNetCore.WebUtilities - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:05:45.692371402 Z + - Microsoft.AspNetCore.Authentication.JwtBearer + - :versions: + - 8.0.14 + :when: 2022-10-14T23:36:49.751Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - - :approve - - Microsoft.Bcl.AsyncInterfaces - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 1.1.1 - :when: 2022-08-16 23:05:46.134783341 Z + - Microsoft.AspNetCore.TestHost + - :versions: + - 8.0.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - - :approve - Microsoft.Bcl.AsyncInterfaces - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:05:46.589Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:46.589538298 Z - - :approve - - Microsoft.CSharp - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.0.1 - :when: 2022-08-16 23:05:47.027783271 Z -- - :approve - - Microsoft.CSharp - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:05:47.470239070 Z + - Microsoft.Bcl.HashCode + - :versions: + - 1.1.1 + :when: 2023-08-04T00:02:30.206Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.CSharp - - :who: mocsharp + - :versions: + - 4.7.0 + :when: 2022-08-16T23:05:47.910Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 4.7.0 - :when: 2022-08-16 23:05:47.910358457 Z +- - :approve + - Microsoft.CodeAnalysis.Analyzers + - :versions: + - 3.3.3 + :when: 2024-01-04T11:05:47.910Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/roslyn-analyzers/raw/main/License.txt) +- - :approve + - Microsoft.CodeAnalysis.CSharp + - :versions: + - 4.5.0 + :when: 2024-01-04T11:05:47.910Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/roslyn/raw/main/License.txt) +- - :approve + - Microsoft.CodeAnalysis.CSharp.Workspaces + - :versions: + - 4.5.0 + :when: 2024-01-04T11:05:47.910Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/roslyn/raw/main/License.txt) +- - :approve + - Microsoft.CodeAnalysis.Common + - :versions: + - 4.5.0 + :when: 2024-01-04T11:05:47.910Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/roslyn/raw/main/License.txt) +- - :approve + - Microsoft.CodeAnalysis.Workspaces.Common + - :versions: + - 4.5.0 + :when: 2024-01-04T11:05:47.910Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/roslyn/raw/main/License.txt) - - :approve - Microsoft.CodeCoverage - - :who: mocsharp - :why: MIT (https://github.com/microsoft/vstest/raw/main/LICENSE) - :versions: - - 17.1.0 - - 17.2.0 - - 17.4.0 - :when: 2022-08-16 23:05:48.342748414 Z + - :versions: + - 17.13.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/microsoft/vstest/raw/v17.4.1/LICENSE) - - :approve - Microsoft.Data.Sqlite.Core - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:49.698463427 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:49.698Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:50.137694970 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:50.137Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Abstractions - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:51.008105271 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:51.008Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Analyzers - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:51.445711308 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:51.445Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Design - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:51.922790944 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:51.922Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.InMemory - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:52.375150938 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:52.375Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Relational - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:52.828879230 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:52.828Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) +- - :approve + - Microsoft.EntityFrameworkCore.Tools + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:52.828Z + :who: ndsouth + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Sqlite - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:53.270526921 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:53.270Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.EntityFrameworkCore.Sqlite.Core - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/release/6.0/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-16 23:05:53.706997823 Z + - :versions: + - 8.0.14 + :when: 2022-08-16T23:05:53.706Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - Microsoft.Extensions.ApiDescription.Server - - :who: mocsharp + - :versions: + - 6.0.5 + - 8.0.0 + :when: 2022-08-16T23:05:54.147Z + :who: mocsharp :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - :versions: - - 6.0.5 - :when: 2022-08-16 23:05:54.147788019 Z - - :approve - Microsoft.Extensions.Caching.Abstractions - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:05:54.613Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:54.613161099 Z - - :approve - Microsoft.Extensions.Caching.Memory - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.1 - :when: 2022-08-16 23:05:55.084861001 Z -- - :approve - - Microsoft.Extensions.Configuration - - :who: mocsharp + - :versions: + - 8.0.1 + :when: 2022-08-16T23:05:55.084Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:55.527814785 Z - - :approve - Microsoft.Extensions.Configuration - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:38.225Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.1 - :when: 2022-08-16 23:05:55.977031585 Z - - :approve - Microsoft.Extensions.Configuration.Abstractions - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:38.225Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:56.428678166 Z - - :approve - Microsoft.Extensions.Configuration.Binder - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + - 8.0.2 + :when: 2022-08-16T23:05:56.869Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:56.869915708 Z - - :approve - Microsoft.Extensions.Configuration.CommandLine - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:57.317738091 Z -- - :approve - - Microsoft.Extensions.Configuration.EnvironmentVariables - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:05:57.317Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:57.756429175 Z - - :approve - Microsoft.Extensions.Configuration.EnvironmentVariables - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T23:05:57.756Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.1 - :when: 2022-08-16 23:05:58.210886620 Z - - :approve - Microsoft.Extensions.Configuration.FileExtensions - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.1 + :when: 2022-08-16T23:05:58.646Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:58.646414000 Z - - :approve - Microsoft.Extensions.Configuration.Json - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:59.140939154 Z -- - :approve - - Microsoft.Extensions.Configuration.UserSecrets - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.1 + :when: 2022-08-16T23:05:59.140Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:05:59.570260365 Z - - :approve - Microsoft.Extensions.Configuration.UserSecrets - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T23:05:59.570Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.1 - :when: 2022-08-16 23:06:00.023322781 Z -- - :approve - - Microsoft.Extensions.DependencyInjection - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:06:00.485562894 Z - - :approve - Microsoft.Extensions.DependencyInjection - - :who: mocsharp + - :versions: + - 6.0.1 + - 8.0.0 + - 8.0.1 + :when: 2022-08-16T23:06:00.904Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - - 6.0.1 - :when: 2022-08-16 23:06:00.904189459 Z -- - :approve - - Microsoft.Extensions.DependencyInjection.Abstractions - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:06:01.336363873 Z - - :approve - Microsoft.Extensions.DependencyInjection.Abstractions - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.2 + :when: 2022-08-16T21:39:41.552Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:01.778427777 Z - - :approve - Microsoft.Extensions.DependencyModel - - :who: mocsharp + - :versions: + - 8.0.2 + :when: 2022-08-16T23:06:02.270Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:02.270628975 Z +- - :approve + - Microsoft.Extensions.Diagnostics + - :versions: + - 8.0.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) +- - :approve + - Microsoft.Extensions.Diagnostics.Abstractions + - :versions: + - 8.0.1 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - - :approve - Microsoft.Extensions.Diagnostics.HealthChecks - - :who: mocsharp + - :versions: + - 8.0.14 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - :versions: - - 6.0.10 - - 6.0.11 - :when: 2022-08-29 18:11:22.090772006 Z - - :approve - Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions - - :who: mocsharp + - :versions: + - 8.0.14 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - :versions: - - 6.0.10 - - 6.0.11 - :when: 2022-08-29 18:11:22.090772006 Z - - :approve - Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore - - :who: mocsharp + - :versions: + - 8.0.14 + :when: 2022-08-29T18:11:22.090Z + :who: mocsharp :why: MIT (https://github.com/dotnet/aspnetcore/raw/main/LICENSE.txt) - :versions: - - 6.0.11 - :when: 2022-08-29 18:11:22.090772006 Z - - :approve - Microsoft.Extensions.FileProviders.Abstractions - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:41.978Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - Microsoft.Extensions.Hosting + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:43.643Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - Microsoft.Extensions.Hosting.Abstractions + - :versions: + - 6.0.0 + - 8.0.1 + :when: 2022-08-16T21:39:43.643Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:02.711721543 Z - - :approve - Microsoft.Extensions.FileProviders.Physical - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T23:06:03.153Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:03.153109366 Z - - :approve - Microsoft.Extensions.FileSystemGlobbing - - :who: mocsharp + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T23:06:03.598Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:03.598159964 Z - - :approve - - Microsoft.Extensions.Hosting - - :who: mocsharp + - Microsoft.Extensions.Logging + - :versions: + - 6.0.0 + - 8.0.0 + - 8.0.1 + :when: 2022-08-16T21:39:44.471Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:04.046151373 Z - - :approve - - Microsoft.Extensions.Hosting - - :who: mocsharp + - Microsoft.Extensions.Logging.Abstractions + - :versions: + - 6.0.0 + - 8.0.3 + :when: 2022-08-16T21:39:44.471Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.1 - :when: 2022-08-16 23:06:04.488545906 Z - - :approve - - Microsoft.Extensions.Hosting.Abstractions - - :who: mocsharp + - Microsoft.Extensions.Logging.Configuration + - :versions: + - 6.0.0 + - 8.0.1 + :when: 2022-08-16T23:06:07.178Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:04.935613050 Z -- - :approve - - Microsoft.Extensions.Http - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:05.375869736 Z -- - :approve - - Microsoft.Extensions.Logging - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:05.840705669 Z -- - :approve - - Microsoft.Extensions.Logging.Abstractions - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - - 6.0.1 - - 6.0.2 - - 6.0.3 - :when: 2022-08-16 23:06:06.728283354 Z -- - :approve - - Microsoft.Extensions.Logging.Configuration - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:07.178037938 Z - - :approve - Microsoft.Extensions.Logging.Console - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:07.618Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:07.618174405 Z - - :approve - Microsoft.Extensions.Logging.Debug - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:08.061Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:08.061150392 Z - - :approve - Microsoft.Extensions.Logging.EventLog - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:08.503Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:08.503025043 Z - - :approve - Microsoft.Extensions.Logging.EventSource - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:08.971Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:08.971793852 Z -- - :approve - - Microsoft.Extensions.ObjectPool - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:06:09.417876763 Z -- - :approve - - Microsoft.Extensions.Options - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:06:09.875478409 Z - - :approve - - Microsoft.Extensions.Options - - :who: mocsharp + - Microsoft.Extensions.Http + - :versions: + - 8.0.0 + :when: 2022-08-16T23:06:05.375Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:10.315793104 Z - - :approve - - Microsoft.Extensions.Options.ConfigurationExtensions - - :who: mocsharp + - Microsoft.Extensions.Options + - :versions: + - 6.0.0 + - 8.0.2 + :when: 2022-08-16T21:39:46.980Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:10.759567780 Z - - :approve - Microsoft.Extensions.Primitives - - :who: mocsharp - :why: Apache-2.0 (https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.0 - :when: 2022-08-16 23:06:11.220257662 Z + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:47.818Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - Microsoft.Extensions.Primitives - - :who: mocsharp + - Microsoft.IdentityModel.Abstractions + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:14.398Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.IdentityModel.JsonWebTokens + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:14.398Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.IdentityModel.Logging + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:15.196Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.IdentityModel.Protocols + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:16.007Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.IdentityModel.Protocols.OpenIdConnect + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:16.403Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.IdentityModel.Tokens + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:16.793Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - Microsoft.NET.ILLink.Tasks + - :versions: + - 8.0.14 + :when: 2022-10-14T23:37:16.793Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:11.669234037 Z - - :approve - Microsoft.NET.Test.Sdk - - :who: mocsharp - :why: MIT (https://raw.githubusercontent.com/microsoft/vstest/main/LICENSE) - :versions: - - 17.4.0 - :when: 2022-09-01 23:06:13.008314524 Z + - :versions: + - 17.13.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/microsoft/vstest/raw/v17.4.1/LICENSE) - - :approve - Microsoft.NETCore.Platforms - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License (http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 1.1.0 - :when: 2022-08-16 23:06:13.457444370 Z + - :versions: + - 1.1.0 + :when: 2022-08-16T21:39:48.664Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) +- - :approve + - Microsoft.OpenApi + - :versions: + - 1.6.23 + :when: 2022-08-16T23:06:15.708Z + :who: mocsharp + :why: MIT ( https://raw.githubusercontent.com/Microsoft/OpenAPI.NET/master/LICENSE) +- - :approve + - Microsoft.Win32.Registry + - :versions: + - 5.0.0 + :when: 2022-11-16T23:38:53.540Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - Microsoft.NETCore.Platforms - - :who: mocsharp + - :versions: + - 5.0.0 + :when: 2022-08-16T23:06:13.902Z + :who: mocsharp :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 3.0.0 - - 5.0.0 - :when: 2022-08-16 23:06:13.902743611 Z -- - :approve - - Microsoft.NETCore.Targets - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 1.1.0 - :when: 2022-08-16 23:06:14.351517365 Z - - :approve - Microsoft.NETCore.Targets - - :who: mocsharp + - :versions: + - 1.1.0 + :when: 2022-08-16T21:39:49.129Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 1.1.3 - :when: 2022-08-16 23:06:14.798049603 Z -- - :approve - - Microsoft.Net.Http.Headers - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/aspnet/AspNetCore/2.0.0/LICENSE.txt) - :versions: - - 2.2.8 - :when: 2022-08-16 23:06:15.238479186 Z -- - :approve - - Microsoft.OpenApi - - :who: mocsharp - :why: MIT ( https://raw.githubusercontent.com/Microsoft/OpenAPI.NET/master/LICENSE) - :versions: - - 1.2.3 - :when: 2022-08-16 23:06:15.708769534 Z - - :approve - Microsoft.TestPlatform.ObjectModel - - :who: mocsharp - :why: MIT (https://github.com/microsoft/vstest/raw/v17.1.0/LICENSE) - :versions: - - 17.1.0 - - 17.2.0 - - 17.4.0 - :when: 2022-08-16 23:06:16.175705981 Z + - :versions: + - 17.13.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/microsoft/vstest/raw/v17.4.1/LICENSE) - - :approve - Microsoft.TestPlatform.TestHost - - :who: mocsharp - :why: MIT (https://github.com/microsoft/vstest/raw/v17.1.0/LICENSE) - :versions: - - 17.1.0 - - 17.2.0 - - 17.4.0 - :when: 2022-08-16 23:06:17.671459450 Z -- - :approve - - Microsoft.Toolkit.HighPerformance - - :who: mocsharp - :why: MIT (https://github.com/CommunityToolkit/WindowsCommunityToolkit/raw/main/License.md) - :versions: - - 7.1.2 - :when: 2022-09-20 00:42:18.619013325 Z -- - :approve - - Microsoft.Toolkit.HighPerformance - - :who: mocsharp - :why: MIT (https://github.com/CommunityToolkit/WindowsCommunityToolkit/raw/main/License.md) - :versions: - - 7.1.2 - :when: 2022-09-20 00:42:18.619013325 Z + - :versions: + - 17.13.0 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: MIT (https://github.com/microsoft/vstest/raw/v17.4.1/LICENSE) - - :approve - Microsoft.Win32.Primitives - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:50.378Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:19.143002793 Z -- - :approve - - Microsoft.Win32.SystemEvents - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:06:19.647254594 Z - - :approve - Minio - - :who: mocsharp + - :versions: + - 6.0.2 + :when: 2022-08-16T18:11:34.443Z + :who: mocsharp :why: Apache-2.0 (https://github.com/minio/minio-dotnet/raw/master/LICENSE) - :versions: - - 4.0.5 - - 4.0.6 - :when: 2022-08-16 23:06:20.598551507 Z - - :approve - Monai.Deploy.Messaging - - :who: neilsouth + - :versions: + - 2.0.4 + :when: 2023-10-13T18:06:21.511Z + :who: neilsouth :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-messaging/raw/main/LICENSE) - :versions: - - 0.1.16 - :when: 2022-08-16 23:06:21.051573547 Z - - :approve - Monai.Deploy.Messaging.RabbitMQ - - :who: neilsouth + - :versions: + - 2.0.4 + :when: 2023-10-13T18:06:21.511Z + :who: neilsouth :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-messaging/raw/main/LICENSE) - :versions: - - 0.1.16 - :when: 2022-08-16 23:06:21.511789690 Z - - :approve - Monai.Deploy.Storage - - :who: mocsharp + - :versions: + - 1.0.2 + :when: 2022-08-16T23:06:21.988Z + :who: mocsharp :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-storage/raw/main/LICENSE) - :versions: - - 0.2.10 - :when: 2022-08-16 23:06:21.988183476 Z - - :approve - Monai.Deploy.Storage.MinIO - - :who: mocsharp + - :versions: + - 1.0.2 + :when: 2022-08-16T23:06:22.426Z + :who: mocsharp :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-storage/raw/main/LICENSE) - :versions: - - 0.2.10 - :when: 2022-08-16 23:06:22.426838304 Z - - :approve - Monai.Deploy.Storage.S3Policy - - :who: mocsharp + - :versions: + - 1.0.2 + :when: 2022-08-16T23:06:22.881Z + :who: mocsharp :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-storage/raw/main/LICENSE) - :versions: - - 0.2.10 - :when: 2022-08-16 23:06:22.881956546 Z - - :approve - - Moq - - :who: mocsharp - :why: BSD 3-Clause License ( https://raw.githubusercontent.com/moq/moq4/main/License.txt) - :versions: - - 4.18.1 - :when: 2022-08-16 23:06:23.359197359 Z -- - :approve - - Moq - - :who: mocsharp - :why: BSD 3-Clause License ( https://raw.githubusercontent.com/moq/moq4/main/License.txt) - :versions: - - 4.18.2 - :when: 2022-08-16 23:06:23.843184482 Z + - Monai.Deploy.Security + - :versions: + - 1.0.1 + :when: 2022-08-16T23:06:21.051Z + :who: mocsharp + :why: Apache-2.0 (https://github.com/Project-MONAI/monai-deploy-security/raw/develop/LICENSE) - - :approve - - NETStandard.Library - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 1.6.1 - :when: 2022-08-16 23:06:24.297968578 Z + - MongoDB.Bson + - :versions: + - 2.30.0 + :when: 2022-11-16T23:38:53.891Z + :who: mocsharp + :why: Apache-2.0 (https://raw.githubusercontent.com/mongodb/mongo-csharp-driver/master/LICENSE.md) - - :approve - - NETStandard.Library - - :who: mocsharp - :why: MIT (https://github.com/dotnet/standard/raw/release/2.0.0/LICENSE.TXT) - :versions: - - 2.0.0 - :when: 2022-08-16 23:06:24.756106826 Z + - MongoDB.Driver + - :versions: + - 2.30.0 + :when: 2022-11-16T23:38:54.213Z + :who: mocsharp + :why: Apache-2.0 (https://raw.githubusercontent.com/mongodb/mongo-csharp-driver/master/LICENSE.md) - - :approve - - NPOI - - :who: mocsharp - :why: Apache-2.0 (https://github.com/nissl-lab/npoi/raw/master/LICENSE) - :versions: - - 2.5.6 - :when: 2022-08-16 23:06:25.198846196 Z + - Microsoft.Extensions.Options.ConfigurationExtensions + - :versions: + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T23:06:10.759Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - Newtonsoft.Json - - :who: mocsharp - :why: MIT (https://github.com/JamesNK/Newtonsoft.Json/raw/master/LICENSE.md) - :versions: - - 10.0.1 - :when: 2022-08-16 23:06:25.648852748 Z + - MongoDB.Driver.Core + - :versions: + - 2.30.0 + :when: 2022-11-16T23:38:54.553Z + :who: mocsharp + :why: Apache-2.0 (https://raw.githubusercontent.com/mongodb/mongo-csharp-driver/master/LICENSE.md) - - :approve - - Newtonsoft.Json - - :who: mocsharp - :why: MIT (https://github.com/JamesNK/Newtonsoft.Json/raw/master/LICENSE.md) - :versions: - - 13.0.1 - :when: 2022-08-16 23:06:26.098713272 Z + - MongoDB.Libmongocrypt + - :versions: + - 1.12.0 + :when: 2022-11-16T23:38:54.863Z + :who: mocsharp + :why: Apache-2.0 (https://raw.githubusercontent.com/mongodb/mongo-csharp-driver/master/LICENSE.md) +- - :approve + - Mono.TextTemplating + - :versions: + - 2.2.1 + :when: 2022-11-16T23:38:54.863Z + :who: mocsharp + :why: MIT (https://github.com/mono/t4/raw/main/LICENSE) - - :approve - - Newtonsoft.Json - - :who: mocsharp - :why: MIT (https://github.com/JamesNK/Newtonsoft.Json/raw/master/LICENSE.md) - :versions: - - 9.0.1 - :when: 2022-08-16 23:06:26.545584451 Z + - Moq + - :versions: + - 4.20.70 + :when: 2023-08-16T16:49:26.950Z + :who: woodheadio + :why: BSD 3-Clause License ( https://raw.githubusercontent.com/moq/moq4/main/License.txt) - - :approve - - Newtonsoft.Json.Bson - - :who: mocsharp - :why: MIT (https://github.com/JamesNK/Newtonsoft.Json.Bson/raw/master/LICENSE.md) - :versions: - - 1.0.1 - :when: 2022-08-16 23:06:27.012580404 Z + - NLog + - :versions: + - 5.4.0 + :when: 2022-10-12T03:14:06.538Z + :who: mocsharp + :why: BSD 3-Clause License (https://github.com/NLog/NLog/raw/dev/LICENSE.txt) - - :approve - - NuGet.Frameworks - - :who: mocsharp - :why: Apache-2.0 (https://github.com/NuGet/NuGet.Client/raw/dev/LICENSE.txt) - :versions: - - 5.11.0 - :when: 2022-08-16 23:06:27.464713741 Z + - NLog.Extensions.Logging + - :versions: + - 5.4.0 + :when: 2022-10-12T03:14:06.964Z + :who: mocsharp + :why: BSD 2-Clause Simplified License (https://github.com/NLog/NLog.Extensions.Logging/raw/master/LICENSE) - - :approve - - Polly - - :who: mocsharp - :why: New BSD License (https://github.com/App-vNext/Polly/raw/master/LICENSE.txt) - :versions: - - 7.2.3 - :when: 2022-08-16 23:06:27.913122244 Z + - NLog.Web.AspNetCore + - :versions: + - 5.4.0 + :when: 2022-10-12T03:14:07.396Z + :who: mocsharp + :why: BSD 3-Clause License (https://github.com/NLog/NLog.Web/raw/master/LICENSE) - - :approve - - Portable.BouncyCastle - - :who: mocsharp - :why: MIT ( https://www.bouncycastle.org/csharp/licence.html) - :versions: - - 1.8.9 - :when: 2022-08-16 23:06:28.368060282 Z + - NETStandard.Library + - :versions: + - 1.6.1 + :when: 2022-08-16T21:39:51.206Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - - :approve - - RabbitMQ.Client - - :who: mocsharp - :why: Apache-2.0 (https://github.com/rabbitmq/rabbitmq-dotnet-client/raw/main/LICENSE-APACHE2) - :versions: - - 6.4.0 - :when: 2022-08-16 23:06:28.818109746 Z + - Newtonsoft.Json + - :versions: + - 13.0.1 + - 13.0.3 + :when: 2022-08-16T21:39:51.628Z + :who: mocsharp + :why: MIT (https://github.com/JamesNK/Newtonsoft.Json/raw/master/LICENSE.md) - - :approve - SQLitePCLRaw.bundle_e_sqlite3 - - :who: mocsharp + - :versions: + - 2.1.6 + :when: 2022-08-16T23:06:29.241Z + :who: mocsharp :why: Apache-2.0 (https://github.com/ericsink/SQLitePCL.raw/raw/master/LICENSE.TXT) - :versions: - - 2.0.6 - :when: 2022-08-16 23:06:29.241291848 Z - - :approve - SQLitePCLRaw.core - - :who: mocsharp + - :versions: + - 2.1.6 + :when: 2022-08-16T23:06:29.688Z + :who: mocsharp :why: Apache-2.0 (https://github.com/ericsink/SQLitePCL.raw/raw/master/LICENSE.TXT) - :versions: - - 2.0.6 - :when: 2022-08-16 23:06:29.688111783 Z - - :approve - SQLitePCLRaw.lib.e_sqlite3 - - :who: mocsharp + - :versions: + - 2.1.6 + :when: 2022-08-16T23:06:30.132Z + :who: mocsharp :why: Apache-2.0 (https://github.com/ericsink/SQLitePCL.raw/raw/master/LICENSE.TXT) - :versions: - - 2.0.6 - :when: 2022-08-16 23:06:30.132403902 Z - - :approve - SQLitePCLRaw.provider.e_sqlite3 - - :who: mocsharp + - :versions: + - 2.1.6 + :when: 2022-08-16T23:06:30.585Z + :who: mocsharp :why: Apache-2.0 (https://github.com/ericsink/SQLitePCL.raw/raw/master/LICENSE.TXT) - :versions: - - 2.0.6 - :when: 2022-08-16 23:06:30.585184235 Z - - :approve - - SharpZipLib - - :who: mocsharp - :why: MIT (https://github.com/icsharpcode/SharpZipLib/raw/master/LICENSE.txt) - :versions: - - 1.3.3 - :when: 2022-08-16 23:06:31.028484905 Z + - Snappier + - :versions: + - 1.0.0 + :when: 2022-10-14T23:37:36.642Z + :who: mocsharp + :why: BSD-3-Clause (https://github.com/brantburnett/Snappier/raw/main/COPYING.txt) +- - :approve + - SharpCompress + - :versions: + - 0.30.1 + :when: 2022-11-16T23:38:55.192Z + :who: mocsharp + :why: MIT (https://github.com/adamhathcock/sharpcompress/raw/master/LICENSE.txt) - - :approve - SpecFlow - - :who: mocsharp - :why: New BSD License (https://github.com/SpecFlowOSS/SpecFlow/raw/master/LICENSE.txt) - :versions: - - 3.9.74 - :when: 2022-08-16 23:06:31.491813346 Z + - :versions: + - 3.9.74 + :when: 2022-08-16T23:06:31.491Z + :who: mocsharp + :why: New BSD License (https://spdx.org/licenses/BSD-3-Clause.html) - - :approve - SpecFlow.Internal.Json - - :who: mocsharp - :why: MIT (https://github.com/SpecFlowOSS/SpecFlow.Internal.Json/raw/master/LICENSE) - :versions: - - 1.0.8 - :when: 2022-08-16 23:06:31.986496283 Z + - :versions: + - 1.0.8 + :when: 2022-08-16T23:06:31.986Z + :who: mocsharp + :why: MIT (https://spdx.org/licenses/BSD-3-Clause.html) - - :approve - SpecFlow.Plus.LivingDocPlugin - - :who: mocsharp - :why: New BSD License (https://github.com/SpecFlowOSS/SpecFlow/raw/master/LICENSE.txt) - :versions: - - 3.9.57 - :when: 2022-08-16 23:06:32.460398443 Z + - :versions: + - 3.9.57 + :when: 2022-08-16T23:06:32.460Z + :who: mocsharp + :why: New BSD License (https://spdx.org/licenses/BSD-3-Clause.html) - - :approve - SpecFlow.Tools.MsBuild.Generation - - :who: mocsharp - :why: New BSD License (https://github.com/SpecFlowOSS/SpecFlow/raw/master/LICENSE.txt) - :versions: - - 3.9.74 - :when: 2022-08-16 23:06:32.914789058 Z + - :versions: + - 3.9.74 + :when: 2022-08-16T23:06:32.914Z + :who: mocsharp + :why: New BSD License (https://spdx.org/licenses/BSD-3-Clause.html) - - :approve - SpecFlow.xUnit - - :who: mocsharp - :why: New BSD License (https://github.com/SpecFlowOSS/SpecFlow/raw/master/LICENSE.txt) - :versions: - - 3.9.74 - :when: 2022-08-16 23:06:33.372884065 Z + - :versions: + - 3.9.74 + :when: 2022-08-16T23:06:33.372Z + :who: mocsharp + :why: New BSD License (https://spdx.org/licenses/BSD-3-Clause.html) - - :approve - Swashbuckle.AspNetCore - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:06:33.817Z + :who: mocsharp :why: MIT (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/raw/master/LICENSE) - :versions: - - 6.4.0 - :when: 2022-08-16 23:06:33.817705411 Z - - :approve - Swashbuckle.AspNetCore.Swagger - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:06:34.264Z + :who: mocsharp :why: MIT (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/raw/master/LICENSE) - :versions: - - 6.4.0 - :when: 2022-08-16 23:06:34.264757523 Z - - :approve - Swashbuckle.AspNetCore.SwaggerGen - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:06:34.716Z + :who: mocsharp :why: MIT (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/raw/master/LICENSE) - :versions: - - 6.4.0 - :when: 2022-08-16 23:06:34.716116883 Z - - :approve - Swashbuckle.AspNetCore.SwaggerUI - - :who: mocsharp + - :versions: + - 8.0.0 + :when: 2022-08-16T23:06:35.164Z + :who: mocsharp :why: MIT (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/raw/master/LICENSE) - :versions: - - 6.4.0 - :when: 2022-08-16 23:06:35.164249703 Z - - :approve - - System.AppContext - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:35.616826259 Z + - NuGet.Frameworks + - :versions: + - 6.5.0 + :when: 2022-08-16T21:39:52.061Z + :who: mocsharp + :why: Apache-2.0 (https://github.com/NuGet/NuGet.Client/raw/dev/LICENSE.txt) - - :approve - - System.Buffers - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:36.066158483 Z + - Polly + - :versions: + - 8.4.0 + - 8.5.2 + :when: 2022-11-09T18:57:32.294Z + :who: mocsharp + :why: MIT ( https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) +- - :approve + - Polly.Core + - :versions: + - 8.4.0 + - 8.5.2 + :when: 2022-11-09T18:57:32.294Z + :who: mocsharp + :why: MIT ( https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - - System.Buffers - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.1 - :when: 2022-08-16 23:06:36.511256664 Z + - RabbitMQ.Client + - :versions: + - 6.8.1 + :when: 2022-08-16T21:39:52.474Z + :who: mocsharp + :why: Apache-2.0 (https://github.com/rabbitmq/rabbitmq-dotnet-client/raw/main/LICENSE-APACHE2) - - :approve - - System.Collections - - :who: mocsharp + - System.AppContext + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:52.894Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:36.963316512 Z - - :approve - - System.Collections.Concurrent - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:37.420188946 Z + - System.Buffers + - :versions: + - 4.3.0 + - 4.5.1 + :when: 2022-08-16T21:39:53.322Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) +- - :approve + - System.CodeDom + - :versions: + - 4.4.0 + :when: 2022-08-16T21:39:53.322Z + :who: mocsharp + :why: MIT ( https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - - :approve - System.Collections.Immutable - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:37.885Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:37.885858733 Z -- - :approve - - System.Collections.NonGeneric - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:38.333847649 Z -- - :approve - - System.Collections.Specialized - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:38.789202996 Z - - :approve - System.CommandLine - - :who: mocsharp + - :versions: + - 2.0.0-beta4.22272.1 + :when: 2022-08-16T23:06:39.201Z + :who: mocsharp :why: MIT (https://github.com/dotnet/command-line-api/raw/main/LICENSE.md) - :versions: - - 2.0.0-beta4.22272.1 - :when: 2022-08-16 23:06:39.201706331 Z - - :approve - System.CommandLine.Hosting - - :who: mocsharp + - :versions: + - 0.4.0-alpha.22272.1 + :when: 2022-08-16T23:06:39.663Z + :who: mocsharp :why: MIT (https://github.com/dotnet/command-line-api/raw/main/LICENSE.md) - :versions: - - 0.4.0-alpha.22272.1 - :when: 2022-08-16 23:06:39.663166680 Z - - :approve - System.CommandLine.NamingConventionBinder - - :who: mocsharp + - :versions: + - 2.0.0-beta4.22272.1 + :when: 2022-08-16T23:06:40.127Z + :who: mocsharp :why: MIT (https://github.com/dotnet/command-line-api/raw/main/LICENSE.md) - :versions: - - 2.0.0-beta4.22272.1 - :when: 2022-08-16 23:06:40.127130435 Z - - :approve - System.CommandLine.Rendering - - :who: mocsharp + - :versions: + - 0.4.0-alpha.22272.1 + :when: 2022-08-16T23:06:40.580Z + :who: mocsharp :why: MIT (https://github.com/dotnet/command-line-api/raw/main/LICENSE.md) - :versions: - - 0.4.0-alpha.22272.1 - :when: 2022-08-16 23:06:40.580085686 Z - - :approve - - System.ComponentModel - - :who: mocsharp + - System.Collections + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:54.169Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:41.023605543 Z -- - :approve - - System.ComponentModel.Annotations - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:06:41.481488759 Z -- - :approve - - System.ComponentModel.Annotations - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 5.0.0 - :when: 2022-08-16 23:06:41.944451098 Z -- - :approve - - System.ComponentModel.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:42.393536803 Z - - :approve - - System.ComponentModel.TypeConverter - - :who: mocsharp + - System.Collections.Concurrent + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:54.601Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:42.868721834 Z -- - :approve - - System.Configuration.ConfigurationManager - - :who: mocsharp - :why: MIT ( https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:06:43.335979768 Z - - :approve - System.Console - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:43.790988552 Z - - :approve - System.Diagnostics.Debug - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:55.889Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:44.247110250 Z -- - :approve - - System.Diagnostics.DiagnosticSource - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:44.697068615 Z - - :approve - System.Diagnostics.DiagnosticSource - - :who: mocsharp + - :versions: + - 4.3.0 + - 6.0.0 + - 8.0.0 + :when: 2022-08-16T21:39:56.310Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:45.141361948 Z - - :approve - System.Diagnostics.EventLog - - :who: mocsharp + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:56.734Z + :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:06:45.597825555 Z - - :approve - System.Diagnostics.Tools - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:57.149Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:46.046733015 Z - - :approve - System.Diagnostics.Tracing - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:46.512191360 Z -- - :approve - - System.Drawing.Common - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:06:46.958823153 Z -- - :approve - - System.Dynamic.Runtime - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.0.11 - :when: 2022-08-16 23:06:47.416090147 Z -- - :approve - - System.Dynamic.Runtime - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:57.568Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:47.870298502 Z - - :approve - System.Globalization - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:57.992Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:48.351904637 Z - - :approve - System.Globalization.Calendars - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:58.421Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:48.816801957 Z - - :approve - System.Globalization.Extensions - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:58.878Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:49.237248446 Z - - :approve - System.IO - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:39:59.280Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:49.709349587 Z - - :approve - System.IO.Abstractions - - :who: mocsharp + - :versions: + - 21.3.1 + :when: 2022-12-14T12:28:00.728Z + :who: samrooke :why: MIT (https://github.com/TestableIO/System.IO.Abstractions/raw/main/LICENSE) - :versions: - - 17.2.1 - - 17.2.3 - :when: 2022-08-16 23:06:50.602318269 Z - - :approve - System.IO.Abstractions.TestingHelpers - - :who: mocsharp + - :versions: + - 21.3.1 + :when: 2022-12-14T12:28:00.728Z + :who: samrooke :why: MIT (https://github.com/TestableIO/System.IO.Abstractions/raw/main/LICENSE) - :versions: - - 17.2.1 - - 17.2.3 - :when: 2022-08-16 23:06:51.524564913 Z - - :approve - System.IO.Compression - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:00.565Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:51.984085743 Z - - :approve - System.IO.Compression.ZipFile - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:01.013Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:52.443609672 Z - - :approve - System.IO.FileSystem - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:01.433Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:52.894925276 Z - - :approve - System.IO.FileSystem.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:53.356642197 Z -- - :approve - - System.Linq - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:53.837042740 Z + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:02.283Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) +- - :approve + - System.IO.Pipelines + - :versions: + - 6.0.3 + - 8.0.0 + :when: 2022-08-16T23:06:53.356Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - System.Linq.Async - - :who: mocsharp + - :versions: + - 6.0.1 + :when: 2022-08-16T23:06:54.280Z + :who: mocsharp :why: MIT (https://github.com/dotnet/reactive/raw/main/LICENSE) - :versions: - - 6.0.1 - :when: 2022-08-16 23:06:54.280123540 Z - - :approve - - System.Linq.Expressions - - :who: mocsharp + - System.IdentityModel.Tokens.Jwt + - :versions: + - 7.1.2 + :when: 2022-10-14T23:37:56.206Z + :who: mocsharp + :why: MIT (https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/raw/dev/LICENSE.txt) +- - :approve + - System.Linq + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:02.703Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:54.757028877 Z - - :approve - - System.Memory - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.1 - :when: 2022-08-16 23:06:55.212678404 Z + - System.Linq.Expressions + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:03.119Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - - :approve - System.Memory - - :who: mocsharp + - :versions: + - 4.5.5 + :when: 2022-08-16T21:40:03.543Z + :who: mocsharp :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.4 - :when: 2022-08-16 23:06:55.672403035 Z -- - :approve - - System.Net.Http - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:56.146126058 Z - - :approve - System.Net.Http - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.4 - :when: 2022-08-16 23:06:56.599420591 Z -- - :approve - - System.Net.NameResolution - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.4 + :when: 2022-08-16T21:40:03.993Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:57.061395380 Z - - :approve - - System.Net.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:57.515482658 Z + - System.Reactive + - :versions: + - 6.0.0 + :when: 2022-08-16T23:06:59.849Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/reactive/raw/main/LICENSE) - - :approve - System.Net.Primitives - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:04.417Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.1 - :when: 2022-08-16 23:06:57.978353117 Z - - :approve - System.Net.Sockets - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:04.847Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:58.430570828 Z - - :approve - System.ObjectModel - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:58.928908929 Z -- - :approve - - System.Private.Uri - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:05.684Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:06:59.382471132 Z -- - :approve - - System.Reactive - - :who: mocsharp - :why: MIT (https://github.com/dotnet/reactive/raw/main/LICENSE) - :versions: - - 5.0.0 - :when: 2022-08-16 23:06:59.849937437 Z -- - :approve - - System.Reactive.Linq - - :who: mocsharp - :why: MIT (https://github.com/dotnet/reactive/raw/main/LICENSE) - :versions: - - 5.0.0 - :when: 2022-08-16 23:07:00.301865283 Z - - :approve - System.Reflection - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:06.105Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:00.774912239 Z - - :approve - System.Reflection.Emit - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:06.527Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:01.233784676 Z - - :approve - System.Reflection.Emit.ILGeneration - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:06.948Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:01.700190013 Z - - :approve - System.Reflection.Emit.Lightweight - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:07.376Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:02.198989958 Z - - :approve - System.Reflection.Extensions - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:07.793Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:02.660700392 Z - - :approve - System.Reflection.Metadata - - :who: mocsharp + - :versions: + - 1.6.0 + - 6.0.1 + :when: 2022-08-16T21:40:08.221Z + :who: mocsharp :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 1.6.0 - :when: 2022-08-16 23:07:03.120522282 Z -- - :approve - - System.Reflection.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:03.573971058 Z - - :approve - - System.Reflection.TypeExtensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:04.045468423 Z + - System.Runtime.CompilerServices.Unsafe + - :versions: + - 6.0.0 + :when: 2022-08-16T23:07:06.347Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - System.Resources.ResourceManager - - :who: mocsharp + - System.Runtime.Loader + - :versions: + - 4.3.0 + :when: 2022-08-16T23:07:08.646Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:04.510917760 Z - - :approve - - System.Runtime - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:04.967405911 Z + - System.Security.AccessControl + - :versions: + - 5.0.0 + :when: 2022-08-16T23:07:11.063Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - - :approve - - System.Runtime - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.1 - :when: 2022-08-16 23:07:05.426503590 Z + - System.Security.Permissions + - :versions: + - 4.5.0 + :when: 2022-08-16T23:07:15.681Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - - :approve - - System.Runtime.CompilerServices.Unsafe - - :who: mocsharp + - System.Text.Encoding.CodePages + - :versions: + - 6.0.1 + :when: 2022-08-16T23:07:17.991Z + :who: mocsharp :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.1 - :when: 2022-08-16 23:07:05.893799510 Z - - :approve - - System.Runtime.CompilerServices.Unsafe + - System.Text.Encodings.Web - :who: mocsharp :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) :versions: - 6.0.0 - :when: 2022-08-16 23:07:06.347562690 Z + - 8.0.0 + :when: 2022-08-16 23:07:19.377530263 Z - - :approve - - System.Runtime.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:06.806629733 Z + - System.Text.Json + - :versions: + - 8.0.5 + :when: 2022-08-16T23:07:20.787Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - System.Runtime.Handles - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:07.266021950 Z + - System.Security.Principal.Windows + - :versions: + - 5.0.0 + :when: 2022-08-16T23:07:17.059Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - System.Runtime.InteropServices - - :who: mocsharp + - System.Security.Cryptography.ProtectedData + - :versions: + - 4.4.0 + - 4.5.0 + :when: 2022-08-16T23:07:14.759Z + :who: mocsharp + :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) +- - :approve + - System.Reflection.Primitives + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:08.640Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:07.723619378 Z - - :approve - - System.Runtime.InteropServices.RuntimeInformation - - :who: mocsharp + - System.Reflection.TypeExtensions + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:09.096Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:08.188254082 Z - - :approve - - System.Runtime.Loader - - :who: mocsharp + - System.Resources.ResourceManager + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:09.522Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:08.646709109 Z - - :approve - - System.Runtime.Numerics - - :who: mocsharp + - System.Runtime + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:09.951Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:09.168158855 Z - - :approve - - System.Runtime.Serialization.Formatters - - :who: mocsharp + - System.Runtime.Extensions + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:10.795Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:09.642273748 Z - - :approve - - System.Runtime.Serialization.Primitives - - :who: mocsharp + - System.Runtime.Handles + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:11.214Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.1.1 - :when: 2022-08-16 23:07:10.146543477 Z - - :approve - - System.Runtime.Serialization.Primitives - - :who: mocsharp + - System.Runtime.InteropServices + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:11.634Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:10.608285434 Z - - :approve - - System.Security.AccessControl - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - - 5.0.0 - :when: 2022-08-16 23:07:11.063425328 Z + - System.Runtime.InteropServices.RuntimeInformation + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:12.076Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - - :approve - - System.Security.Claims - - :who: mocsharp + - System.Runtime.Numerics + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:12.501Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:11.524172409 Z - - :approve - System.Security.Cryptography.Algorithms - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:13.349Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:11.995074441 Z - - :approve - System.Security.Cryptography.Cng - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:13.768Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:12.460027529 Z - - :approve - System.Security.Cryptography.Csp - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:14.196Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:12.915683406 Z - - :approve - System.Security.Cryptography.Encoding - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:14.641Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:13.372792205 Z - - :approve - System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:15.059Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:13.833567880 Z - - :approve - System.Security.Cryptography.Primitives - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:15.494Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:14.288309340 Z -- - :approve - - System.Security.Cryptography.ProtectedData - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:07:14.759818552 Z - - :approve - System.Security.Cryptography.X509Certificates - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:15.916Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:15.218020510 Z -- - :approve - - System.Security.Permissions - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.0 - :when: 2022-08-16 23:07:15.681971110 Z - - :approve - - System.Security.Principal - - :who: mocsharp + - System.Text.Encoding + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:16.760Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:16.144884093 Z - - :approve - - System.Security.Principal.Windows - - :who: mocsharp + - System.Text.Encoding.Extensions + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:17.179Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:16.594561848 Z -- - :approve - - System.Security.Principal.Windows - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 4.5.0 - - 5.0.0 - :when: 2022-08-16 23:07:17.059464936 Z -- - :approve - - System.Text.Encoding - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:17.528153938 Z -- - :approve - - System.Text.Encoding.CodePages - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.6.0 - :when: 2022-08-16 23:07:17.991171210 Z -- - :approve - - System.Text.Encoding.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:18.447188540 Z -- - :approve - - System.Text.Encodings.Web - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.7.2 - :when: 2022-08-16 23:07:18.927665553 Z -- - :approve - - System.Text.Encodings.Web - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:07:19.377530263 Z -- - :approve - - System.Text.Json - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.7.2 - :when: 2022-08-16 23:07:19.845361666 Z -- - :approve - - System.Text.Json - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - - 6.0.5 - - 6.0.7 - :when: 2022-08-16 23:07:20.787263056 Z - - :approve - System.Text.RegularExpressions - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:18.441Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:21.228544774 Z - - :approve - System.Threading - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:18.900Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:21.683762371 Z -- - :approve - - System.Threading.Channels - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 4.7.1 - :when: 2022-08-16 23:07:22.192780172 Z - - :approve - System.Threading.Channels - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:07:22.653576384 Z -- - :approve - - System.Threading.Overlapped - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:23.112954683 Z + - :versions: + - 6.0.0 + - 7.0.0 + - 8.0.0 + :when: 2022-08-16T21:40:19.306Z + :who: mocsharp + :why: MIT ( https://github.com/dotnet/corefx/blob/master/LICENSE.TXT) - - :approve - System.Threading.Tasks - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:23.571361753 Z -- - :approve - - System.Threading.Tasks.Dataflow - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 6.0.0 - :when: 2022-08-16 23:07:24.033230543 Z -- - :approve - - System.Threading.Tasks.Extensions - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:19.741Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:24.492365116 Z - - :approve - System.Threading.Tasks.Extensions - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.5.4 - :when: 2022-08-16 23:07:24.949679540 Z -- - :approve - - System.Threading.ThreadPool - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:25.418534550 Z + - :versions: + - 4.3.0 + - 4.5.4 + :when: 2022-08-16T21:40:20.164Z + :who: mocsharp + :why: MIT (https://dotnet.microsoft.com/en-us/dotnet_library_license.htm) - - :approve - System.Threading.Timer - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:20.589Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:25.883963589 Z - - :approve - - System.ValueTuple - - :who: mocsharp - :why: MIT (https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - :versions: - - 4.4.0 - :when: 2022-08-16 23:07:26.336720827 Z + - Validation + - :versions: + - 2.4.18 + :when: 2022-08-16T23:07:28.177Z + :who: mocsharp + :why: Microsoft Public License ( https://raw.githubusercontent.com/AArnott/Validation/69e6a2c4f3/LICENSE.txt) - - :approve - System.Xml.ReaderWriter - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:21.012Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:26.793971546 Z - - :approve - System.Xml.XDocument - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:21.430Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:27.254750074 Z - - :approve - - System.Xml.XmlDocument - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:27.710993680 Z + - TestableIO.System.IO.Abstractions + - :versions: + - 21.3.1 + :when: 2022-08-16T21:40:21.430Z + :who: mocsharp + :why: MIT (https://github.com/TestableIO/System.IO.Abstractions/raw/main/LICENSE) - - :approve - - Validation - - :who: mocsharp - :why: Microsoft Public License ( https://raw.githubusercontent.com/AArnott/Validation/69e6a2c4f3/LICENSE.txt) - :versions: - - 2.4.18 - :when: 2022-08-16 23:07:28.177073804 Z + - TestableIO.System.IO.Abstractions.TestingHelpers + - :versions: + - 21.3.1 + :when: 2022-08-16T21:40:21.430Z + :who: mocsharp + :why: MIT (https://github.com/TestableIO/System.IO.Abstractions/raw/main/LICENSE) - - :approve - - Xunit.SkippableFact - - :who: mocsharp - :why: Microsoft Public License ( https://raw.githubusercontent.com/AArnott/Xunit.SkippableFact/c7f20eaa78/LICENSE.txt) - :versions: - - 1.3.12 - :when: 2022-08-16 23:07:28.614499928 Z + - TestableIO.System.IO.Abstractions.Wrappers + - :versions: + - 21.3.1 + :when: 2022-08-16T21:40:21.430Z + :who: mocsharp + :why: MIT (https://github.com/TestableIO/System.IO.Abstractions/raw/main/LICENSE) +- - :approve + - ZstdSharp.Port + - :versions: + - 0.7.3 + :when: 2022-10-14T23:38:32.685Z + :who: mocsharp + :why: MIT (https://github.com/oleg-st/ZstdSharp/raw/master/LICENSE) - - :approve - coverlet.collector - - :who: mocsharp + - :versions: + - 6.0.4 + :when: 2022-08-16T21:40:21.855Z + :who: mocsharp :why: MIT (https://github.com/coverlet-coverage/coverlet/raw/master/LICENSE) - :versions: - - 3.2.0 - :when: 2022-08-16 23:07:29.112978564 Z - - :approve - fo-dicom - - :who: mocsharp + - :versions: + - 5.2.1 + :when: 2022-08-16T23:07:29.574Z + :who: mocsharp :why: Microsoft Public License (https://github.com/fo-dicom/fo-dicom/raw/development/License.txt) - :versions: - - 5.0.2 - - 5.0.3 - :when: 2022-08-16 23:07:29.574869349 Z -- - :approve - - runtime.any.System.Collections - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:30.069677397 Z -- - :approve - - runtime.any.System.Diagnostics.Tools - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:30.529945784 Z -- - :approve - - runtime.any.System.Diagnostics.Tracing - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:31.021730702 Z -- - :approve - - runtime.any.System.Globalization - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:31.481132711 Z -- - :approve - - runtime.any.System.Globalization.Calendars - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:31.945695771 Z -- - :approve - - runtime.any.System.IO - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:32.403765835 Z -- - :approve - - runtime.any.System.Reflection - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:32.858655636 Z -- - :approve - - runtime.any.System.Reflection.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:33.315602333 Z -- - :approve - - runtime.any.System.Reflection.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:33.771768025 Z -- - :approve - - runtime.any.System.Resources.ResourceManager - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:34.228810352 Z -- - :approve - - runtime.any.System.Runtime - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:34.690529850 Z -- - :approve - - runtime.any.System.Runtime.Handles - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:35.150542193 Z -- - :approve - - runtime.any.System.Runtime.InteropServices - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:35.620394499 Z -- - :approve - - runtime.any.System.Text.Encoding - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:36.100093192 Z -- - :approve - - runtime.any.System.Text.Encoding.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:36.572772075 Z -- - :approve - - runtime.any.System.Threading.Tasks - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:37.042733737 Z -- - :approve - - runtime.any.System.Threading.Timer - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:37.510163706 Z -- - :approve - - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:37.975510010 Z - - :approve - runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:38.442665116 Z -- - :approve - - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:22.289Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:38.916484375 Z - - :approve - runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:39.378413588 Z -- - :approve - - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:22.712Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:39.846759614 Z - - :approve - runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:23.140Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:40.310122568 Z - - :approve - runtime.native.System - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:23.578Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:40.770387468 Z - - :approve - runtime.native.System.IO.Compression - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:23.998Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:41.256353878 Z - - :approve - runtime.native.System.Net.Http - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:24.434Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:41.735502132 Z - - :approve - runtime.native.System.Security.Cryptography.Apple - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:42.215870949 Z -- - :approve - - runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:24.868Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:42.677550528 Z - - :approve - runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:43.146802503 Z -- - :approve - - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:25.303Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:43.607437995 Z - - :approve - runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:44.063686661 Z -- - :approve - - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:25.719Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:44.531336835 Z - - :approve - runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:26.152Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:44.993904411 Z - - :approve - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:45.456102215 Z -- - :approve - - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + :when: 2022-08-16T21:40:26.579Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:45.914491629 Z - - :approve - runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:46.387398978 Z -- - :approve - - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:27.001Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:46.875356107 Z - - :approve - runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:47.338714846 Z -- - :approve - - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:27.431Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:47.799517867 Z - - :approve - runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:48.261167359 Z -- - :approve - - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:27.853Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:48.721519740 Z - - :approve - runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:49.217063386 Z -- - :approve - - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:28.265Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:49.744187064 Z - - :approve - runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.2 - :when: 2022-08-16 23:07:50.215973953 Z -- - :approve - - runtime.unix.Microsoft.Win32.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:50.699325453 Z -- - :approve - - runtime.unix.System.Console - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:51.169241694 Z -- - :approve - - runtime.unix.System.Diagnostics.Debug - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:51.636258264 Z -- - :approve - - runtime.unix.System.IO.FileSystem - - :who: mocsharp + - :versions: + - 4.3.0 + - 4.3.2 + :when: 2022-08-16T21:40:28.686Z + :who: mocsharp :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:52.162021614 Z -- - :approve - - runtime.unix.System.Net.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:52.626421521 Z -- - :approve - - runtime.unix.System.Net.Sockets - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:53.095872278 Z -- - :approve - - runtime.unix.System.Private.Uri - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:53.570244807 Z -- - :approve - - runtime.unix.System.Runtime.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:54.028175638 Z -- - :approve - - runtime.win.Microsoft.Win32.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:54.501591322 Z -- - :approve - - runtime.win.System.Console - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:54.973922104 Z -- - :approve - - runtime.win.System.Diagnostics.Debug - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:55.444537287 Z -- - :approve - - runtime.win.System.IO.FileSystem - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:55.920296048 Z -- - :approve - - runtime.win.System.Net.Primitives - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:56.376768598 Z -- - :approve - - runtime.win.System.Net.Sockets - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:56.838741736 Z -- - :approve - - runtime.win.System.Runtime.Extensions - - :who: mocsharp - :why: MICROSOFT .NET LIBRARY License ( http://go.microsoft.com/fwlink/?LinkId=329770) - :versions: - - 4.3.0 - :when: 2022-08-16 23:07:57.326794959 Z - - :approve - xRetry - - :who: mocsharp + - :versions: + - 1.9.0 + :when: 2022-08-16T23:07:57.794Z + :who: mocsharp :why: MIT (https://github.com/JoshKeegan/xRetry/raw/master/LICENSE) - :versions: - - 1.8.0 - :when: 2022-08-16 23:07:57.794503140 Z -- - :approve - - xunit - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.1 - :when: 2022-08-16 23:07:58.264039741 Z - - :approve - xunit - - :who: mocsharp + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:29.166Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.2 - :when: 2022-08-16 23:07:58.741042809 Z - - :approve - xunit.abstractions - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.0.3 - :when: 2022-08-16 23:07:59.230841326 Z -- - :approve - - xunit.analyzers - - :who: mocsharp + - :versions: + - 2.0.3 + :when: 2022-08-16T21:40:29.587Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 0.10.0 - :when: 2022-08-16 23:07:59.702393969 Z - - :approve - xunit.analyzers - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 1.0.0 - :when: 2022-08-16 23:08:00.165216213 Z + - :versions: + - 1.14.0 + :when: 2022-08-16T21:40:30.047Z + :who: mocsharp + :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit.analyzers/master/LICENSE) - - :approve - xunit.assert - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.1 - :when: 2022-08-16 23:08:00.634240281 Z -- - :approve - - xunit.assert - - :who: mocsharp + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:30.526Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.2 - :when: 2022-08-16 23:08:01.105384447 Z - - :approve - xunit.core - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.1 - :when: 2022-08-16 23:08:01.570300282 Z -- - :approve - - xunit.core - - :who: mocsharp + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:30.973Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.2 - :when: 2022-08-16 23:08:02.057792372 Z - - :approve - xunit.extensibility.core - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.1 - :when: 2022-08-16 23:08:02.535203327 Z -- - :approve - - xunit.extensibility.core - - :who: mocsharp + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:31.401Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.2 - :when: 2022-08-16 23:08:03.019024760 Z - - :approve - xunit.extensibility.execution - - :who: mocsharp - :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.1 - :when: 2022-08-16 23:08:03.493713542 Z -- - :approve - - xunit.extensibility.execution - - :who: mocsharp + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:31.845Z + :who: mocsharp :why: Apache-2.0 ( https://raw.githubusercontent.com/xunit/xunit/master/license.txt) - :versions: - - 2.4.2 - :when: 2022-08-16 23:08:03.959558421 Z - - :approve - xunit.runner.visualstudio - - :who: mocsharp - :why: MIT ( https://licenses.nuget.org/MIT) - :versions: - - 2.4.3 - :when: 2022-08-16 23:08:04.429394853 Z -- - :approve - - xunit.runner.visualstudio - - :who: mocsharp - :why: MIT ( https://licenses.nuget.org/MIT) - :versions: - - 2.4.5 - :when: 2022-08-16 23:08:04.892608686 Z -- - :approve - - Ardalis.GuardClauses - - :who: mocsharp - :why: MIT (https://github.com/ardalis/GuardClauses.Analyzers/raw/master/LICENSE) - :versions: - - 4.0.1 - :when: 2022-08-16 23:10:21.184627612 Z -- - :approve - - NLog - - :who: mocsharp - :why: BSD 3-Clause License (https://github.com/NLog/NLog/raw/dev/LICENSE.txt) - :versions: - - 5.0.5 - :when: 2022-10-12 03:14:06.538744982 Z -- - :approve - - NLog.Extensions.Logging - - :who: mocsharp - :why: BSD 2-Clause Simplified License (https://github.com/NLog/NLog.Extensions.Logging/raw/master/LICENSE) - :versions: - - 5.1.0 - :when: 2022-10-12 03:14:06.964203977 Z -- - :approve - - NLog.Web.AspNetCore - - :who: mocsharp - :why: BSD 3-Clause License (https://github.com/NLog/NLog.Web/raw/master/LICENSE) - :versions: - - 5.1.5 - :when: 2022-10-12 03:14:07.396706995 Z -- - :approve - - fo-dicom.NLog - - :who: mocsharp - :why: Microsoft Public License (https://github.com/fo-dicom/fo-dicom/raw/development/License.txt) - :versions: - - 5.0.3 - :when: 2022-10-12 03:14:08.789273776 Z -- - :approve - - DnsClient - - :who: mocsharp - :why: Apache-2.0 (https://github.com/MichaCo/DnsClient.NET/raw/dev/LICENSE) - :versions: - - 1.6.1 - :when: 2022-11-16 23:33:33.315560769 Z -- - :approve - - Snappier - - :who: mocsharp - :why: BSD-3-Clause (https://github.com/brantburnett/Snappier/raw/main/COPYING.txt) - :versions: - - 1.0.0 - :when: 2022-10-14 23:37:36.642306800 Z -- - :approve - - ZstdSharp.Port - - :who: mocsharp - :why: MIT (https://github.com/oleg-st/ZstdSharp/raw/master/LICENSE) - :versions: - - 0.6.2 - :when: 2022-10-14 23:38:32.685243680 Z -- - :approve - - Microsoft.Win32.Registry - - :who: mocsharp - :why: MIT (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - :versions: - - 5.0.0 - :when: 2022-11-16 23:38:53.540718932 Z -- - :approve - - MongoDB.Bson - - :who: mocsharp - :why: Apache-2.0 (https://github.com/mongodb/mongo-csharp-driver/raw/master/License.txt) - :versions: - - 2.18.0 - :when: 2022-11-16 23:38:53.891380809 Z -- - :approve - - MongoDB.Driver - - :who: mocsharp - :why: Apache-2.0 (https://github.com/mongodb/mongo-csharp-driver/raw/master/License.txt) - :versions: - - 2.18.0 - :when: 2022-11-16 23:38:54.213853364 Z -- - :approve - - MongoDB.Driver.Core - - :who: mocsharp - :why: Apache-2.0 (https://github.com/mongodb/mongo-csharp-driver/raw/master/License.txt) - :versions: - - 2.18.0 - :when: 2022-11-16 23:38:54.553730219 Z + - :versions: + - 2.8.1 + :when: 2022-08-16T21:40:32.294Z + :who: mocsharp + :why: MIT ( https://raw.githubusercontent.com/dotnet/efcore/refs/heads/main/LICENSE.txt) - - :approve - - MongoDB.Libmongocrypt - - :who: mocsharp - :why: Apache-2.0 (https://github.com/mongodb/mongo-csharp-driver/raw/master/License.txt) - :versions: - - 1.6.0 - :when: 2022-11-16 23:38:54.863359236 Z + - Xunit.SkippableFact + - :versions: + - 1.3.12 + :when: 2022-08-16T23:07:28.614Z + :who: mocsharp + :why: Microsoft Public License (https://raw.githubusercontent.com/AArnott/Xunit.SkippableFact/c7f20eaa78/LICENSE.txt) +- - :approve + - System.Composition + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - System.Composition.AttributedModel + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - System.Composition.Convention + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - System.Composition.Hosting + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - System.Composition.Runtime + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) +- - :approve + - System.Composition.TypedParts + - :versions: + - 6.0.0 + :when: 2022-08-16T21:39:55.442Z + :who: mocsharp + :why: MICROSOFT .NET LIBRARY License (https://github.com/dotnet/runtime/raw/main/LICENSE.TXT) - - :approve - - SharpCompress - - :who: mocsharp - :why: MIT (https://github.com/adamhathcock/sharpcompress/raw/master/LICENSE.txt) - :versions: - - 0.30.1 - :when: 2022-11-16 23:38:55.192078193 Z + - System.Configuration.ConfigurationManager + - :versions: + - 4.4.0 + - 4.5.0 + :when: 2022-08-16T23:06:43.335Z + :who: mocsharp + :why: MIT ( https://github.com/dotnet/corefx/raw/master/LICENSE.TXT) - - :approve - - SharpCompress - - :who: mocsharp - :why: MIT (https://github.com/adamhathcock/sharpcompress/raw/master/LICENSE.txt) - :versions: - - 0.30.1 - :when: 2022-11-16 23:38:55.532789254 Z + - System.IO.Hashing + - :versions: + - 8.0.0 + :when: 2023-08-10T20:50:14.759Z + :who: mocsharp + :why: MIT (https://raw.githubusercontent.com/dotnet/runtime/main/LICENSE.TXT) diff --git a/docker-compose/configs/orthanc.json b/docker-compose/configs/orthanc.json old mode 100644 new mode 100755 index 7023954b0..ef34c7020 --- a/docker-compose/configs/orthanc.json +++ b/docker-compose/configs/orthanc.json @@ -191,7 +191,21 @@ "STORESCP", "127.0.0.1", 104 + ], + "mig-local-1104": [ + "STORESCP", + "host.docker.internal", + 1104 + ], + + "mig-local-1104StartMWMExtApp": [ + "StartMWMExtApp", + "host.docker.internal", + 1104 ] + + + /** * By default, the Orthanc SCP accepts all DICOM commands (C-ECHO, * C-STORE, C-FIND, C-MOVE) issued by the registered remote SCU @@ -512,4 +526,4 @@ "StowMaxSize": 10, // For STOW-RS client, the maximum size of the body in one single HTTP query (in MB, 0 = no limit) "QidoCaseSensitive": true // For QIDO-RS server, whether search is case sensitive (since release 0.5) } -} \ No newline at end of file +} diff --git a/docker-compose/configs/pipeline.yml b/docker-compose/configs/pipeline.yml index a3ab98fdc..7dfdf98d7 100644 --- a/docker-compose/configs/pipeline.yml +++ b/docker-compose/configs/pipeline.yml @@ -11,6 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +<<<<<<<< HEAD:docker-compose/configs/pipeline.yml - pipeline.id: monei-deploy path.config: "/usr/share/logstash/pipeline/monai-deploy.conf" queue.type: persisted +======== + +general: + vulnerabilities: + - CVE-2018-8292 + - CVE-1999-1278 +>>>>>>>> 442df4cd (Merge patch release/0.3.2 into main):.github/containerscan/allowedlist.yaml diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml old mode 100644 new mode 100755 index b20931435..d0c2fd7e7 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -15,7 +15,7 @@ version: "3.9" services: rabbitmq: - image: rabbitmq:3.10-management + image: rabbitmq:3.13-management hostname: rabbitmq ports: - 5672:5672 @@ -35,7 +35,7 @@ services: - monaideploy minio: - image: "minio/minio:latest" + image: "minio/minio:RELEASE.2024-06-11T03-13-30Z" command: server --console-address ":9001" /data hostname: minio volumes: @@ -75,7 +75,7 @@ services: start_period: 40s orthanc: - image: osimis/orthanc:22.9.0 + image: osimis/orthanc:24.1.2-full hostname: orthanc volumes: - ./configs/orthanc.json:/etc/orthanc/orthanc.json diff --git a/docs/api/rest/config.md b/docs/api/rest/config.md index 874b9bebe..ad760d1cf 100644 --- a/docs/api/rest/config.md +++ b/docs/api/rest/config.md @@ -1,5 +1,5 @@ + +# DICOM Association information + +The `/dicom-associations' endpoint is for retrieving a list of information regarding dicom +associations. + +## GET /dicom-associations/ + +#### Query Parameters + +| Name | Type | Description | +|------------|----------|---------------------------------------------| +| startTime | DateTime | (Optional) Start date to query from. | +| endTime | DateTime | (Optional) End date to query from. | +| pageNumber | Number | (Optional) Page number to query.(default 0) | +| pageSize | Number | (Optional) Page size of query. | + +Max & Defaults for PageSize can be set in appSettings. + +```json +"endpointSettings": { + "defaultPageSize": number, + "maxPageSize": number +} +``` + +Endpoint returns a paged result for example + +```json +{ + "PageNumber": 1, + "PageSize": 10, + "FirstPage": "/payload?pageNumber=1&pageSize=10", + "LastPage": "/payload?pageNumber=1&pageSize=10", + "TotalPages": 1, + "TotalRecords": 3, + "NextPage": null, + "PreviousPage": null, + "Data": [...] + "Succeeded": true, + "Errors": null, + "Message": null +} +``` diff --git a/docs/api/rest/dicomweb-stow.md b/docs/api/rest/dicomweb-stow.md index 2d4eadbc8..1b870c031 100644 --- a/docs/api/rest/dicomweb-stow.md +++ b/docs/api/rest/dicomweb-stow.md @@ -18,12 +18,11 @@ The `dicomweb/` endpoint implements the specifications defined in [section 6.6 STOW-RS Request/Response](https://dicom.nema.org/dicom/2013/output/chtml/part18/sect_6.6.html#sect_6.6.1.3.2.1.1) defined by the DICOM committee to provide the [DICOMWeb STOW-RS](https://www.dicomstandard.org/using/dicomweb/store-stow-rs) -interface for triggering new workflows. - +interface for triggering new workflows. The *STOW-RS* service provides the following two endpoints. -## POST /dicomweb/studies/[{study-instance-uid}] +## POST /dicomweb/studies/[{study-instance-uid}/] Triggers a new workflow request with the uploaded DICOM dataset. @@ -31,15 +30,20 @@ Triggers a new workflow request with the uploaded DICOM dataset. > Each HTTP POST request triggers a new workflow request; the service *does not* support waiting for additional instances like the DIMSE service. +### Example Endpoints + +- `POST /dicomweb/studies/` +- `POST /dicomweb/studies/123.001.123.1.4.976.20160825112022727.3/` + ### Parameters -#### Query Parameters: +#### Query Parameters -| Name | Type | Description | -| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196)` = `B007`. | +| Name | Type | Description | +| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196) = B007`. | -#### Request Body: +#### Request Body Supported Content-Types: @@ -51,35 +55,39 @@ Supported Content-Types: Response Content Type: `JSON` | Code | Data Type | Description | -| ---- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored succesfully. | +| ---- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. | | 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID. | | 204 | `none` | No data is provided. | | 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. | -| 415 | `none` | Unsupported media typ. | +| 415 | `none` | Unsupported media type. | | 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error. | | 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage. | --- - -## POST /dicomweb/{workflow-id}/studies/[{study-instance-uid}] +## POST /dicomweb/{workflow-id}/studies/[{study-instance-uid}/] Triggers the specified workflow with the uploaded DICOM dataset. > [!IMPORTANT] > Each HTTP POST request triggers a new workflow request; the service *does not* support waiting for additional instances like the DIMSE service. +### Example Endpoints + +- `POST /dicomweb/liver-segmentation/studies/` +- `POST /dicomweb/my-awesome-workflow/studies/123.001.123.1.4.976.20160825112022727.3/` + ### Parameters -#### Query Parameters: +#### Query Parameters -| Name | Type | Description | -| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| workflow-id | string | The unique identifier of the workflow registered with the Workflow Manager. | -| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196)` = `B007`. | +| Name | Type | Description | +| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| workflow-id | string | The unique identifier of the workflow registered with the Workflow Manager. | +| study-instance-uid | string | (Optional) Associate the DICOM dataset with a StudyInstanceUID. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196) = B007`. | -#### Request Body: +#### Request Body Supported Content-Types: @@ -92,10 +100,60 @@ Response Content Type: `JSON` | Code | Data Type | Description | | ---- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored succesfully. | +| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. | +| 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID).| +| 204 | `none` | No data is provided. | +| 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. | +| 415 | `none` | Unsupported media type | +| 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error | +| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage | + +--- + +## POST /dicomweb/vae/{aet}/[{workflow-id}/]studies/[{study-instance-uid}/] + +A DICOMWeb STOW-RS endpoint associated with the specified [Virtual Application Entity](xref:Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity). + +This endpoint can either trigger workflows defined in a [Virtual Application Entity](xref:Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity) or trigger the workflow specified in the URL segment where the latter +takes precedence when specified. + +> [!IMPORTANT] +> Each HTTP POST request triggers a new workflow request; the service *does not* support waiting for additional instances like the DIMSE service. + +### Example Endpoints + +- `POST /dicomweb/vae/my-aet/studies/` +- `POST /dicomweb/vae/my-aet/studies/123.001.123.1.4.976.20160825112022727.3/` +- `POST /dicomweb/vae/my-aet/my-awesome-workflow/studies/` +- `POST /dicomweb/vae/my-aet/my-awesome-workflow/studies/123.001.123.1.4.976.20160825112022727.3/` + +### Parameters + +#### Query Parameters + +| Name | Type | Description | +| ------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| aet | string | A registered Virtual Application Entity | +| workflow-id | string | The unique identifier of the workflow registered with the Workflow Manager | +| study-instance-uid | string | (Optional) A StudyInstanceUID to associate the DICOM dataset with. Note that the service records any mismatch between the StudyInstanceUID header and the provided value in the response as `Warning Reason (0008,1196) = B007`.| + +#### Request Body + +Supported Content-Types: + +- `application/dicom` +- `multipart/related` + +### Responses + +Response Content Type: `JSON` + +| Code | Data Type | Description | +| ---- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| 200 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored successfully. | | 202 | [DicomDataset](https://github.com/fo-dicom/fo-dicom/blob/development/FO-DICOM.Core/DicomDataset.cs) | All instances are received and stored with warnings (e.g. for a mismatched StudyInstanceUID. | | 204 | `none` | No data is provided. | | 400 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Request contains invalid values. | | 415 | `none` | Unsupported media type | | 500 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Server error | -| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage. | +| 507 | [Problem details](https://datatracker.ietf.org/doc/html/rfc7807) | Insufficient storage | diff --git a/docs/api/rest/fhir.md b/docs/api/rest/fhir.md index dd5df8f88..a45525e23 100644 --- a/docs/api/rest/fhir.md +++ b/docs/api/rest/fhir.md @@ -14,7 +14,7 @@ ~ limitations under the License. --> -# DICOMWeb STOW-RS APIs +# FHIR APIs The `fhir/` endpoint implements the specifications defined in [section 3.1.0 RESTful API](http://hl7.org/implement/standards/fhir/http.html) defined by HL7 (Health Level 7 International) to enable triggering new workflows. The FHIR service supports multiple versions of the Fast Healthcare Interoperability Resources (FHIR) specifications published by Health Level 7 International (HL7). diff --git a/docs/api/rest/toc.yml b/docs/api/rest/toc.yml index 27d12f266..439ca319d 100644 --- a/docs/api/rest/toc.yml +++ b/docs/api/rest/toc.yml @@ -14,6 +14,10 @@ - name: Configuration href: config.md +- name: DICOMWeb STOW + href: dicomweb-stow.md +- name: FHIR + href: fhir.md - name: Health href: health.md - name: Inference Request diff --git a/docs/changelog.md b/docs/changelog.md index ecefdc3d3..9168dc56b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,5 @@ -# MONAI Deploy Informatics Gateway v0.0.0 +# MONAI Deploy Informatics Gateway ![NVIDIA](./images/MONAI-logo_color.svg) @@ -27,51 +27,36 @@ MIG uses standard protocols like DICOM and FHIR. It stores studies and resources After inference completes, MIG receives notifications for exporting the results to the proper consumers, usually PACS or viewers for visualization, VNAs for storage, and EHRs (Electronic Healthcare Records). -## Services +A list of supported protocols and services are available on the [MONAI Deploy Informatics Gateway Services](./setup/services.md) page. -MIG contains the following standard protocols for communicating with medical devices: -* **DICOM SCP**: C-ECHO, C-STORE -* **DICOM SCU**: C-STORE -* **HL7 Server**: A HL7 MLLP listener. -* **ACR DSI API**: [The American College of Radiology’s Data Science Institute API](https://www.acrdsi.org/-/media/DSI/Files/ACR-DSI-Model-API.pdf) -* **DICOMweb client**: QIDO-RS, WADO-RS, STOW-RS -* **FHIR client**: GET +## Contributing +For guidance on making a contribution, see the [contributing guidelines](https://github.com/Project-MONAI/monai-deploy/blob/main/CONTRIBUTING.md). -[!Note] -The ACR DSI API uses the DICOMweb client and FHIR client. +## Community +To participate, please join the MONAI Deploy App SDK weekly meetings on the [calendar](https://calendar.google.com/calendar/u/0/embed?src=c_954820qfk2pdbge9ofnj5pnt0g@group.calendar.google.com&ctz=America/New_York) and review the [meeting notes](https://docs.google.com/document/d/1nw7JX-1kVaHiK8wBteM96xAWE3dh5wRUeC691bGuFjk/edit?usp=sharing). -### DICOM SCP +Join the conversation on Twitter [@ProjectMONAI](https://twitter.com/ProjectMONAI) or join our [Slack channel](https://forms.gle/QTxJq3hFictp31UM9). -The *DICOM SCP Service* accepts standard DICOM C-ECHO and C-STORE commands, which receive DICOM instances for processing. In addition, the Informatics Gateway groups received DICOM instances by the study or series based on the configuration. Once DICOM instances are grouped, they are assembled into a payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) to consume. +Ask and answer questions over on [MONAI Deploy Informatics Gateway's GitHub Discussions tab](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/discussions). -### DICOM SCU +## License -The *DICOM SCU Service* enables users to export application-generated DICOM results to external DICOM devices. It subscribes to the `md.export.request.monaiscu` events generated by the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) and then exports the data to user-configured DICOM destination(s). +Copyright (c) MONAI Consortium. All rights reserved. +Licensed under the [Apache-2.0](https://github.com/Project-MONAI/monai-deploy-informatics-gateway/blob/develop/LICENSE) license. -> [!Note] -> DICOM instances are sent as-is; no codec conversion is done as part of the SCU process. -> See the [DICOM Interface SCU](./compliance/dicom.md#dimse-services-scu) section for more information. +This software uses the Microsoft .NET 6.0 library, and the use of this software is subject to the [Microsoft software license terms](https://dotnet.microsoft.com/en-us/dotnet_library_license.htm). -### DICOMWeb STOW-RS +By downloading this software, you agree to the license terms and all licenses listed on the [third-party licenses](./compliance/third-party-licenses.md) page. -The *DICOMWeb STOW-RS Service* allows users to trigger a new workflow request by uploading a DICOM dataset. The entire DICOM dataset is assembled into a payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) to consume. -It provides options to trigger a workflow with or without specifying a workflow ID/name. See the -[DICOMWeb STOW-RS](./api/rest/dicomweb-stow.md) section for more information. +## Links -### HL7 MLLP Server +- Website: +- API documentation: +- Code: +- Project tracker: +- Issue tracker: +- Wiki: +- Test status: -The *HL7 MLLP Server* accepts Health Level 7 messages via the MLLP (Minimal Lower Layer Protocol). The received messages are validated and assembled into a payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) to consume. -### ACR DSI API - -The ACR DSI API allows users to trigger inference requests via RESTful calls, utilizing DICOMweb and FHIR to -retrieve data specified in the request. Upon data retrieval, the Informatics Gateway uploads the data to the -shared storage and generates an `md.workflow.request` event, which notifies the -[MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) for processing. - -### DICOMweb Export - -A DICOMweb export agent can export any user-generated DICOM content to configured DICOM destinations. The agent -subscribes to the `md.export.request.monaidicomweb` events generated by the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) -and then exports the data to user-configured DICOMweb destination(s). diff --git a/docs/plug-ins/overview.md b/docs/plug-ins/overview.md new file mode 100644 index 000000000..198197a7b --- /dev/null +++ b/docs/plug-ins/overview.md @@ -0,0 +1,146 @@ + + +# Data Plug-ins + +Data plug-ins enable manipulation of incoming data before they are saved to the storage service or outgoing data right before they are exported. + +## Using Data Plug-ins + +The Informatics Gateway allows you to configure data plug-ins in the following services: + +- (DIMSE) MONAI Deploy DICOM Listener: Configure each listening AE Title with zero or more data plug-ins via the + [CLI](../setup/cli.md) or via the [Configuration API](../api/rest/config.md). +- (DIMSE) DICOM Export: configure the `Plug-inAssemblies` with one or more data plug-ins in the [ExportRequestEvent](https://github.com/Project-MONAI/monai-deploy-messaging/blob/main/src/Messaging/Events/ExportRequestEvent.cs#L85). +- (DICOMWeb) STOW-RS: + - The Virtual AE endpoints (`/dicomweb/vae/...`) can be configured similarly to the DICOM listener using the [DICOMWeb STOW API](../api/rest/dicomweb-stow.md##post-dicomwebvaeaetworkflow-idstudiesstudy-instance-uid). + - For the default `/dicomweb/...` endpoints, set zero or more plug-ins under `InformaticsGateway>dicomWeb>plug-ins` in the + `appsettings.json` [configuration](../setup/schema.md) file. +- (DICOMWeb) Export: configure the `Plug-inAssemblies` with one or more data plug-ins in the [ExportRequestEvent](https://github.com/Project-MONAI/monai-deploy-messaging/blob/main/src/Messaging/Events/ExportRequestEvent.cs#L85). + +> [!Note] +> When one or more plug-ins are defined, the plug-ins are executed in the order as they are listed. + +## Available Plug-ins + +The following plug-ins are available: + +| Name | Description | Fully Qualified Assembly Name | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| [DicomDeidentifier](./remote-app.md) | A plug-in that de-identifies a set of configurable DICOM tags with random data before DICOM data is exported. | `Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.DicomDeidentifier, Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution` | +| [DicomReidentifier](./remote-app.md) | A plug-in to be used together with the `DicomDeidentifier` plug-in to restore the original DICOM metadata. | `Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.DicomReidentifier, Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution` | + + +## Creating a Plug-in + +To create an input data plug-in, implement the [IInputDataPlugin](xref:Monai.Deploy.InformaticsGateway.Api.PlugIns.IInputDataPlugIn) interface and +put the [dynamic link library](https://learn.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library) (DLL) in +the `plug-ins/` directories. Similarly, for output data plug-ins, implement the [IOutputDataPlugin](xref:Monai.Deploy.InformaticsGateway.Api.PlugIns.IOutputDataPlugIn) +interface. + +Refer to the [Configuration API](../api/rest/config.md) page to retrieve available [input](../api/rest/config.md#get-configaeplug-ins) and +[output](../api/rest/config.md#get-configdestinationplug-ins) data plug-ins. + + +### Database Extensions + +If a plug-in requires presistent data in the database, extend the [DatabaseRegistrationBase](xref:Monai.Deploy.InformaticsGateway.Database.Api.DatabaseRegistrationBase) +class to register your database context and repositories. + +Refer to the `Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution` plug-in as a reference. + +> [!Important] +> The Informatics Gateway requires all plug-ins to extend both the Entity Framework (SQLite) and MongoDB databases. + +#### Entity Framework + +Implement the [IDatabaseMigrationManagerForPlugIns](xref:Monai.Deploy.InformaticsGateway.Database.Api.IDatabaseMigrationManagerForPlugIns) interface to +register your Entity Framework (EF) database context. A `connectionString` is provided to the `Configure(...)` function when you extend the +[DatabaseRegistrationBase](xref:Monai.Deploy.InformaticsGateway.Database.Api.DatabaseRegistrationBase) class, allowing you to create your +[code-first](https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/workflows/new-database) EF database context and generate your +migration code using the [dotnet ef](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) CLI tool. + +The following example extends the [DatabaseRegistrationBase](xref:Monai.Deploy.InformaticsGateway.Database.Api.DatabaseRegistrationBase) class +to register a new EF database context named `RemoteAppExecutionDbContext`. + +In the method, you first register the database context, then register your Migration Manager by implementing the +[IDatabaseMigrationManagerForPlugIns](xref:Monai.Deploy.InformaticsGateway.Database.Api.IDatabaseMigrationManagerForPlugIns) interface. +Lastly, you register your repository for the `RemoteAppExecutions` table. + +```csharp +public class DatabaseRegistrar : DatabaseRegistrationBase +{ + public override IServiceCollection Configure(IServiceCollection services, DatabaseType databaseType, string? connectionString) + { + Guard.Against.Null(services, nameof(services)); + + switch (databaseType) + { + case DatabaseType.EntityFramework: + Guard.Against.Null(connectionString, nameof(connectionString)); + services.AddDbContext(options => options.UseSqlite(connectionString), ServiceLifetime.Transient); + services.AddScoped(); + + services.AddScoped(typeof(IRemoteAppExecutionRepository), typeof(EntityFramework.RemoteAppExecutionRepository)); + break; + ... + } + + return services; + } +} +``` + +#### MongoDB + +Similar to the [Entity Framework](#entity-framework) section above, for MongoDB you first register your Migration Manager by implementing +the [IDatabaseMigrationManagerForPlugIns](xref:Monai.Deploy.InformaticsGateway.Database.Api.IDatabaseMigrationManagerForPlugIns) interface +and then register your repository for the `RemoteAppExecutions` collection. + +```csharp +public class DatabaseRegistrar : DatabaseRegistrationBase +{ + public override IServiceCollection Configure(IServiceCollection services, DatabaseType databaseType, string? connectionString) + { + Guard.Against.Null(services, nameof(services)); + + switch (databaseType) + { + case DatabaseType.MongoDb: + services.AddScoped(); + + services.AddScoped(typeof(IRemoteAppExecutionRepository), typeof(MongoDb.RemoteAppExecutionRepository)); + break; + ... + } + + return services; + } +} +``` + +In the `MigrationManager`, configure the `RemoteAppExecutions` collection. + +```csharp +public class MigrationManager : IDatabaseMigrationManagerForPlugIns +{ + public IHost Migrate(IHost host) + { + RemoteAppExecutionConfiguration.Configure(); + return host; + } +} +``` diff --git a/docs/plug-ins/remote-app.md b/docs/plug-ins/remote-app.md new file mode 100644 index 000000000..83fdc3ae7 --- /dev/null +++ b/docs/plug-ins/remote-app.md @@ -0,0 +1,61 @@ + + +# Remote App Execution Plug-ins + +The **Remote App Execution Plug-ins** allow the users to configure a set of DICOM metadata to be replaced with dummy data before +being exported using the `DicomDeidentifier` plug-in. The original data is stored in the database; when the data returns +via DICOM DIMSE or DICOMWeb, the data can be restored using the `DicomReidentifier` plug-in. + +## Supported Data Types + +- DICOM + +## Configuration + +One or more DICOM tags may be configured in the `appsettings.json` file. For example, the following snippet will replace the +`AccessionNumber`, `StudyDescription`, and `SeriesDescription` tags. + +```json +{ + "InformaticsGateway": { + "plugins": { + "remoteApp": { + "ReplaceTags": "AccessionNumber, StudyDescription, SeriesDescription" + } + } + } +} +``` + +Refer to [NEMA](https://dicom.nema.org/medical/dicom/current/output/chtml/part06/chapter_6.html) for a complete list of DICOM tags +and use the value from the **Keyword** column. + +> [!Note] +> `StudyInstanceUID`, `SeriesInstanceUID` and `SOPInstanceUID` are always replaced and tracked to ensure the same +> studies and series get the same UIDs. + +> [!Important] +> Only top-level DICOM metadata can be replaced at this time. + +## Fully Qualified Assembly Names + +The following plug-ins are available: + +| Name | Fully Qualified Assembly Name | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `DicomDeidentifier` | `Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.DicomDeidentifier, Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution` | +| `DicomReidentifier` | `Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.DicomReidentifier, Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution` | diff --git a/docs/plug-ins/toc.yml b/docs/plug-ins/toc.yml new file mode 100644 index 000000000..72462cfec --- /dev/null +++ b/docs/plug-ins/toc.yml @@ -0,0 +1,18 @@ +# Copyright 2021-2022 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Data Plug-ins + href: overview.md +- name: Remote App Execution Plug-ins + href: remote-app.md diff --git a/docs/setup/cli.md b/docs/setup/cli.md index 1c793fa27..69c19cad8 100644 --- a/docs/setup/cli.md +++ b/docs/setup/cli.md @@ -1,5 +1,5 @@ + + +# MONAI Deploy Informatics Gateway Services + +MONAI Deploy Informatics Gateway supports the following standard protocols for communicating with medical devices: + +* **DICOM SCP**: C-ECHO, C-STORE +* **DICOM SCU**: C-STORE +* **HL7 Server**: An HL7 MLLP listener +* **ACR DSI API**: [The American College of Radiology�s Data Science Institute API](https://www.acrdsi.org/-/media/DSI/Files/ACR-DSI-Model-API.pdf) +* **DICOMweb client**: QIDO-RS, WADO-RS, STOW-RS +* **FHIR Server**: POST +* **FHIR client**: GET + +> [!Note] +> The ACR DSI API uses the DICOMweb client and FHIR client. + +## DICOM SCP + +The *DICOM SCP Service* accepts standard DICOM C-ECHO and C-STORE commands, which receive DICOM instances for processing. In addition, +the Informatics Gateway groups received DICOM instances by the study or series based on the configuration. Once DICOM instances are grouped, +they are assembled into a payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) to consume. + +### Workflow Request + +With a DICOM SCP triggered workflow request, the data trigger contains the following: + +- `DataService`: `DataService.DIMSE` +- `Source`: `` +- `Destination`: `` + +## DICOM SCU + +The *DICOM SCU Service* allows users to export application-generated DICOM results to external DICOM devices. It subscribes +to the `md.export.request.monaiscu` events generated by the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager), +then exports the data to user-configured DICOM destination(s). + +> [!Note] +> DICOM instances are sent as-is; no codec conversion is done as part of the SCU process. +> See the [DICOM Interface SCU](../compliance/dicom.md#dimse-services-scu) section for more information. + +## DICOMWeb STOW-RS + +The *DICOMWeb STOW-RS Service* allows users to trigger a new workflow request by uploading a DICOM dataset. The entire DICOM dataset is assembled into a +payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) to consume. +It provides options to trigger a workflow with or without specifying a workflow ID/name. See the +[DICOMWeb STOW-RS](../api/rest/dicomweb-stow.md) section for more information. + +### Workflow Request + +With a DICOMWeb STOW-RS triggered workflow request, the data trigger contains the following: + +- `DataService`: `DataService.DicomWeb` +- `Source`: `` +- `Destination`: `default` or `` + +## HL7 MLLP Server + +The *HL7 MLLP Server* accepts Health Level 7 messages via the MLLP (Minimal Lower Layer Protocol). The received messages are +validated and assembled into a payload for the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) +to consume. + +### Workflow Request + +If a workflow request is triggered by an HL7 MLLP Server, the data trigger contains the following: + +- `DataService`: `DataService.HL7` +- `Source`: `` +- `Destination`: `` + +## ACR DSI API + +The ACR DSI API allows users to trigger inference requests via RESTful calls, utilizing DICOMweb and FHIR to +retrieve data specified in the request. Upon data retrieval, the Informatics Gateway uploads the data to the +shared storage and generates an `md.workflow.request` event, which notifies the +[MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager) for processing. +See the [Inference API](../api/rest/inference.md) for more information. + +### Workflow Request + +If a workflow request is triggered by the ACR DSI API, the data trigger contains the following: + +- `DataService`: `DataService.ACR` +- `Source`: `` +- `Destination`: `` + +## DICOMweb Export + +A DICOMweb export agent can export any user-generated DICOM content to configured DICOM destinations. The agent +subscribes to the `md.export.request.monaidicomweb` events generated by the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager), +then exports the data to user-configured DICOMweb destination(s). + +## FHIR Server + +The *FHIR Server* accepts FHIR resources as described in the [FHIR API](../api/rest/fhir.md) section. When data arrives at the service, +each resource is packaged into a payload and a workflow request is sent to the [MONAI Deploy Workflow Manager](https://github.com/Project-MONAI/monai-deploy-workflow-manager). + + +### Workflow Request + +With an FHIR Server triggered workflow request, the data trigger contains the following: + +- `DataService`: `DataService.FHIR` +- `Source`: `` +- `Destination`: `` diff --git a/docs/setup/setup.md b/docs/setup/setup.md index 308e5decb..53cac5562 100644 --- a/docs/setup/setup.md +++ b/docs/setup/setup.md @@ -1,5 +1,5 @@ - Monai.Deploy.InformaticsGateway.Api - net6.0 + net8.0 9.0 Apache-2.0 true ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + enable + false + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage + + + + + + + + Monai.Deploy.InformaticsGateway.Api + 0.4.1 + MONAI Consortium + MONAI Consortium + true + MONAI Deploy Informatics Gateway API + MONAI Consortium + https://github.com/Project-MONAI/monai-deploy-informatics-gateway + https://github.com/Project-MONAI/monai-deploy-informatics-gateway + Apache-2.0 + True - - - All - + + - - - + + + + - - - + + + + + + - - + \ No newline at end of file diff --git a/src/Api/PlugIns/IInputDataPlugin.cs b/src/Api/PlugIns/IInputDataPlugin.cs new file mode 100644 index 000000000..a6b5a50c8 --- /dev/null +++ b/src/Api/PlugIns/IInputDataPlugin.cs @@ -0,0 +1,34 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading.Tasks; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugIn enables lightweight data processing over incoming data received from supported data ingestion + /// services. + /// Refer to for additional details. + /// + public interface IInputDataPlugIn + { + string Name { get; } + + Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata); + } +} diff --git a/src/Api/PlugIns/IInputDataPluginEngine.cs b/src/Api/PlugIns/IInputDataPluginEngine.cs new file mode 100644 index 000000000..dc349822a --- /dev/null +++ b/src/Api/PlugIns/IInputDataPluginEngine.cs @@ -0,0 +1,42 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugInEngine processes incoming data receivied from various supported services through + /// a list of plug-ins based on . + /// Rules: + /// + /// SCP: A list of plug-ins can be configured with each AET, and each plug-in is executed in the order stored, enabling piping of the incoming data before each file is uploaded to the storage service. + /// Incoming data is processed one file at a time and SHALL not wait for the entire study to arrive. + /// Plug-ins MUST be lightweight and not hinder the upload process. + /// Plug-ins SHALL not accumulate files in memory or storage for bulk processing. + /// + /// + public interface IInputDataPlugInEngine + { + void Configure(IReadOnlyList pluginAssemblies); + + Task> ExecutePlugInsAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata); + } +} diff --git a/src/Api/PlugIns/IInputHL7DataPlugIn.cs b/src/Api/PlugIns/IInputHL7DataPlugIn.cs new file mode 100755 index 000000000..b44d0f736 --- /dev/null +++ b/src/Api/PlugIns/IInputHL7DataPlugIn.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugIn enables lightweight data processing over incoming data received from supported data ingestion + /// services. + /// Refer to for additional details. + /// + public interface IInputHL7DataPlugIn + { + string Name { get; } + + Task<(Message hl7Message, FileStorageMetadata fileMetadata)> ExecuteAsync(Message hl7File, FileStorageMetadata fileMetadata); + } + +} diff --git a/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs b/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs new file mode 100755 index 000000000..dc34b976d --- /dev/null +++ b/src/Api/PlugIns/IInputHL7DataPlugInEngine.cs @@ -0,0 +1,42 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IInputDataPlugInEngine processes incoming data receivied from various supported services through + /// a list of plug-ins based on . + /// Rules: + /// + /// SCP: A list of plug-ins can be configured with each AET, and each plug-in is executed in the order stored, enabling piping of the incoming data before each file is uploaded to the storage service. + /// Incoming data is processed one file at a time and SHALL not wait for the entire study to arrive. + /// Plug-ins MUST be lightweight and not hinder the upload process. + /// Plug-ins SHALL not accumulate files in memory or storage for bulk processing. + /// + /// + public interface IInputHL7DataPlugInEngine + { + void Configure(IReadOnlyList pluginAssemblies); + + Task> ExecutePlugInsAsync(Message hl7File, FileStorageMetadata fileMetadata, Hl7ApplicationConfigEntity configItem); + } +} diff --git a/src/Api/PlugIns/IOutputDataPlugin.cs b/src/Api/PlugIns/IOutputDataPlugin.cs new file mode 100755 index 000000000..8bd695108 --- /dev/null +++ b/src/Api/PlugIns/IOutputDataPlugin.cs @@ -0,0 +1,34 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading.Tasks; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IOutputDataPlugIn enables lightweight data processing over incoming data received from supported data ingestion + /// services. + /// Refer to for additional details. + /// + public interface IOutputDataPlugIn + { + string Name { get; } + + Task<(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage)> ExecuteAsync(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage); + } +} diff --git a/src/Api/PlugIns/IOutputDataPluginEngine.cs b/src/Api/PlugIns/IOutputDataPluginEngine.cs new file mode 100755 index 000000000..080717a3d --- /dev/null +++ b/src/Api/PlugIns/IOutputDataPluginEngine.cs @@ -0,0 +1,39 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; +using System.Threading.Tasks; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + /// + /// IOutputDataPlugInEngine processes each file before exporting to its destination + /// through a list of plug-ins based on . + /// Rules: + /// + /// A list of plug-ins can be included with each export request, and each plug-in is executed in the order stored, processing one file at a time, enabling piping of the data before each file is exported. + /// Plug-ins MUST be lightweight and not hinder the export process. + /// Plug-ins SHALL not accumulate files in memory or storage for bulk processing. + /// + /// + public interface IOutputDataPlugInEngine + { + void Configure(IReadOnlyList pluginAssemblies); + + Task ExecutePlugInsAsync(ExportRequestDataMessage exportRequestDataMessage); + } +} diff --git a/src/Api/PlugIns/PluginNameAttribute.cs b/src/Api/PlugIns/PluginNameAttribute.cs new file mode 100644 index 000000000..46ae56869 --- /dev/null +++ b/src/Api/PlugIns/PluginNameAttribute.cs @@ -0,0 +1,34 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using Ardalis.GuardClauses; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + [AttributeUsage(AttributeTargets.Class)] + public class PlugInNameAttribute : Attribute + { + public string Name { get; set; } + + public PlugInNameAttribute(string name) + { + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + Name = name; + } + } +} diff --git a/src/Api/PlugIns/SR.cs b/src/Api/PlugIns/SR.cs new file mode 100644 index 000000000..57471e027 --- /dev/null +++ b/src/Api/PlugIns/SR.cs @@ -0,0 +1,27 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO; + +namespace Monai.Deploy.InformaticsGateway.Api.PlugIns +{ + public static class SR + { + public const string PlugInDirectoryName = "plug-ins"; + public static readonly string PlugInDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SR.PlugInDirectoryName); + } +} diff --git a/src/Api/Rest/DicomWebConnectionDetails.cs b/src/Api/Rest/DicomWebConnectionDetails.cs index ed27d1586..2f2e32151 100644 --- a/src/Api/Rest/DicomWebConnectionDetails.cs +++ b/src/Api/Rest/DicomWebConnectionDetails.cs @@ -29,20 +29,20 @@ public class DicomWebConnectionDetails /// Gets or sets a list of permitted operations for the connection. /// [JsonPropertyName("operations")] - public IList Operations { get; set; } + public IList? Operations { get; set; } /// /// Gets or sets the resource URI (Uniform Resource Identifier) of the connection. /// [JsonPropertyName("uri")] - public string Uri { get; set; } + public string? Uri { get; set; } /// /// Gets or sets the authentication/authorization token of the connection. /// For HTTP basic access authentication, the value must be encoded in based 64 using "{username}:{password}" format. /// [JsonPropertyName("authID")] - public string AuthId { get; set; } + public string? AuthId { get; set; } /// /// Gets or sets the type of the authentication token used for the connection. diff --git a/src/Api/Rest/FhirResources.cs b/src/Api/Rest/FhirResources.cs index 65c038538..a2a2ae6b6 100644 --- a/src/Api/Rest/FhirResources.cs +++ b/src/Api/Rest/FhirResources.cs @@ -81,14 +81,14 @@ public class FhirResource /// E.g. Pateitn, Observation. /// [JsonPropertyName("resourceType")] - public string Type { get; set; } + public string Type { get; set; } = default!; /// /// Gets or sets the ID of the resource to be retrieved. /// /// [JsonPropertyName("id")] - public string Id { get; set; } + public string Id { get; set; } = default!; /// /// Internal use only! @@ -96,6 +96,6 @@ public class FhirResource /// /// [JsonPropertyName("isRetrieved")] - public bool IsRetrieved { get; set; } + public bool IsRetrieved { get; set; } = default!; } } diff --git a/src/Api/Rest/InferenceRequest.cs b/src/Api/Rest/InferenceRequest.cs index 5af67be63..1a271f41f 100644 --- a/src/Api/Rest/InferenceRequest.cs +++ b/src/Api/Rest/InferenceRequest.cs @@ -19,7 +19,6 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; -using System.Xml.Linq; using Ardalis.GuardClauses; using Monai.Deploy.InformaticsGateway.Common; @@ -83,7 +82,7 @@ public class InferenceRequest /// Gets or set the transaction ID of a request. /// [JsonPropertyName("transactionID")] - public string TransactionId { get; set; } + public string TransactionId { get; set; } = default!; /// /// Gets or sets the priority of a request. @@ -101,7 +100,7 @@ public class InferenceRequest /// Gets or sets the details of the data associated with the inference request. /// [JsonPropertyName("inputMetadata")] - public InferenceRequestMetadata InputMetadata { get; set; } + public InferenceRequestMetadata? InputMetadata { get; set; } /// /// Gets or set a list of data sources to query/retrieve data from. @@ -109,7 +108,7 @@ public class InferenceRequest /// the order the list was received. /// [JsonPropertyName("inputResources")] - public IList InputResources { get; set; } + public IList? InputResources { get; set; } /// /// Gets or set a list of data sources to export results to. @@ -118,7 +117,17 @@ public class InferenceRequest /// Followed by registering the results using the MONAI App SDK. /// [JsonPropertyName("outputResources")] - public IList OutputResources { get; set; } + public IList? OutputResources { get; set; } + + /// + /// Gets or set the user who created the DICOM entity. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or set the date and time the objects first created. + /// + public DateTime? DateTimeCreated { get; set; } #region Internal Use Only @@ -155,11 +164,11 @@ public class InferenceRequest public int TryCount { get; set; } = 0; [JsonIgnore] - public InputConnectionDetails Application + public InputConnectionDetails? Application { get { - return InputResources.FirstOrDefault(predicate => predicate.Interface == InputInterfaceType.Algorithm)?.ConnectionDetails; + return InputResources?.FirstOrDefault(predicate => predicate.Interface == InputInterfaceType.Algorithm)?.ConnectionDetails; } } @@ -169,6 +178,7 @@ public InferenceRequest() { InputResources = new List(); OutputResources = new List(); + DateTimeCreated = DateTime.UtcNow; } public bool IsValid(out string details) @@ -192,7 +202,7 @@ private void Preprocess() if (InputMetadata.Details is not null) { InputMetadata.Inputs.Add(InputMetadata.Details); - InputMetadata.Details = null; + InputMetadata.Details = default!; } } @@ -220,15 +230,15 @@ private bool Validate(out string details) private void ValidateOUtputResources(List errors) { - Guard.Against.Null(errors); + Guard.Against.Null(errors, nameof(errors)); - if (InputMetadata.Inputs.IsNullOrEmpty()) + if (InputMetadata is not null && InputMetadata.Inputs.IsNullOrEmpty()) { errors.Add("Request has no `inputMetadata` defined. At least one `inputs` or `inputMetadata` required."); } - else + else if (InputMetadata!.Inputs is not null) { - foreach (var inputDetails in InputMetadata.Inputs) + foreach (var inputDetails in InputMetadata!.Inputs) { CheckInputMetadataDetails(inputDetails, errors); } @@ -237,9 +247,9 @@ private void ValidateOUtputResources(List errors) private void ValidateInputMetadata(List errors) { - Guard.Against.Null(errors); + Guard.Against.Null(errors, nameof(errors)); - foreach (var output in OutputResources) + foreach (var output in OutputResources ?? Enumerable.Empty()) { if (output.Interface == InputInterfaceType.DicomWeb) { @@ -254,15 +264,15 @@ private void ValidateInputMetadata(List errors) private void ValidateInputResources(List errors) { - Guard.Against.Null(errors); + Guard.Against.Null(errors, nameof(errors)); if (InputResources.IsNullOrEmpty() || - !InputResources.Any(predicate => predicate.Interface != InputInterfaceType.Algorithm)) + !InputResources!.Any(predicate => predicate.Interface != InputInterfaceType.Algorithm)) { errors.Add("No 'inputResources' specified."); } - foreach (var input in InputResources) + foreach (var input in InputResources ?? Enumerable.Empty()) { if (input.Interface == InputInterfaceType.DicomWeb) { @@ -309,14 +319,14 @@ private static void CheckInputMetadataDetails(InferenceRequestDetails details, L private static void CheckInputMetadataWithTypeFhirResource(InferenceRequestDetails details, List errors) { - Guard.Against.Null(details); - Guard.Against.Null(errors); + Guard.Against.Null(details, nameof(details)); + Guard.Against.Null(errors, nameof(errors)); if (details.Resources.IsNullOrEmpty()) { errors.Add("Request type is set to `FHIR_RESOURCE` but no FHIR `resources` defined."); } - else + else if (details.Resources is not null) { errors.AddRange(details.Resources.Where(resource => string.IsNullOrWhiteSpace(resource.Type)).Select(resource => "A FHIR resource type cannot be empty.")); } @@ -324,14 +334,14 @@ private static void CheckInputMetadataWithTypeFhirResource(InferenceRequestDetai private static void CheckInputMetadataWithTypDicomUid(InferenceRequestDetails details, List errors) { - Guard.Against.Null(details); - Guard.Against.Null(errors); + Guard.Against.Null(details, nameof(details)); + Guard.Against.Null(errors, nameof(errors)); if (details.Studies.IsNullOrEmpty()) { errors.Add("Request type is set to `DICOM_UID` but no `studies` defined."); } - else + else if (details.Studies is not null) { foreach (var study in details.Studies) { @@ -348,7 +358,7 @@ private static void CheckInputMetadataWithTypDicomUid(InferenceRequestDetails de private static void CheckInputMetadataWithTypeDicomSeries(List errors, RequestedStudy study) { - foreach (var series in study.Series) + foreach (var series in study.Series ?? Enumerable.Empty()) { if (string.IsNullOrWhiteSpace(series.SeriesInstanceUid)) { @@ -356,31 +366,32 @@ private static void CheckInputMetadataWithTypeDicomSeries(List errors, R } if (series.Instances is null) continue; + errors.AddRange( series.Instances .Where( instance => instance.SopInstanceUid.IsNullOrEmpty() || - instance.SopInstanceUid.Any(p => string.IsNullOrWhiteSpace(p))) + instance.SopInstanceUid!.Any(p => string.IsNullOrWhiteSpace(p))) .Select(instance => "`SOPInstanceUID` cannot be empty.")); } } - private static void CheckFhirConnectionDetails(string source, List errors, DicomWebConnectionDetails connection) + private static void CheckFhirConnectionDetails(string source, List errors, DicomWebConnectionDetails? connection) { - if (!Uri.IsWellFormedUriString(connection.Uri, UriKind.Absolute)) + if (connection is not null && !Uri.IsWellFormedUriString(connection.Uri, UriKind.Absolute)) { errors.Add($"The provided URI '{connection.Uri}' in `{source}` is not well formed."); } } - private static void CheckDicomWebConnectionDetails(string source, List errors, DicomWebConnectionDetails connection) + private static void CheckDicomWebConnectionDetails(string source, List errors, DicomWebConnectionDetails? connection) { - if (connection.AuthType != ConnectionAuthType.None && string.IsNullOrWhiteSpace(connection.AuthId)) + if (connection is not null && connection.AuthType != ConnectionAuthType.None && string.IsNullOrWhiteSpace(connection.AuthId)) { errors.Add($"One of the '{source}' has authType of '{connection.AuthType:F}' but does not include a valid value for 'authId'"); } - if (!Uri.IsWellFormedUriString(connection.Uri, UriKind.Absolute)) + if (connection is not null && !Uri.IsWellFormedUriString(connection.Uri, UriKind.Absolute)) { errors.Add($"The provided URI '{connection.Uri}' is not well formed."); } diff --git a/src/Api/Rest/InferenceRequestDetails.cs b/src/Api/Rest/InferenceRequestDetails.cs index 9af0d7066..58ead9ad8 100644 --- a/src/Api/Rest/InferenceRequestDetails.cs +++ b/src/Api/Rest/InferenceRequestDetails.cs @@ -71,12 +71,12 @@ namespace Monai.Deploy.InformaticsGateway.Api.Rest /// public class InferenceRequestDetails { - private string _fhirAcceptHeader; + private string _fhirAcceptHeader = default!; /// /// Gets or sets the type of the inference request. /// - [JsonConverter(typeof(JsonStringEnumConverter))] + [JsonConverter(typeof(JsonStringEnumMemberConverter))] [JsonPropertyName("type")] public InferenceRequestType Type { get; set; } @@ -85,27 +85,27 @@ public class InferenceRequestDetails /// Used when Type is . /// [JsonPropertyName("studies")] - public IList Studies { get; set; } + public IList? Studies { get; set; } /// /// Gets or sets Patient ID that is used to query the data source. /// Used when Type is . /// [JsonPropertyName("PatientID")] - public string PatientId { get; set; } + public string? PatientId { get; set; } /// /// Gets or sets Access Number that is used to query the data source. /// Used when Type is . /// [JsonPropertyName("accessionNumber")] - public IList AccessionNumber { get; set; } + public IList? AccessionNumber { get; set; } /// /// Gets or sets a list of FHIR resources to be retrived. /// [JsonPropertyName("resources")] - public IList Resources { get; set; } + public IList? Resources { get; set; } /// /// Gets or set the data format used when storing FHIR resources. diff --git a/src/Api/Rest/InferenceRequestException.cs b/src/Api/Rest/InferenceRequestException.cs index 9184afc3d..c3941f904 100644 --- a/src/Api/Rest/InferenceRequestException.cs +++ b/src/Api/Rest/InferenceRequestException.cs @@ -16,14 +16,12 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Api.Rest { /// /// Inference request exception. /// - [Serializable] public class InferenceRequestException : Exception { public InferenceRequestException() @@ -37,9 +35,5 @@ public InferenceRequestException(string message) : base(message) public InferenceRequestException(string message, Exception innerException) : base(message, innerException) { } - - protected InferenceRequestException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/Api/Rest/InferenceRequestMetadata.cs b/src/Api/Rest/InferenceRequestMetadata.cs index bd2449cc2..69166f503 100644 --- a/src/Api/Rest/InferenceRequestMetadata.cs +++ b/src/Api/Rest/InferenceRequestMetadata.cs @@ -44,13 +44,13 @@ public class InferenceRequestMetadata /// Gets or sets the details of an inference request. /// [JsonPropertyName("details")] - public InferenceRequestDetails Details { get; set; } + public InferenceRequestDetails? Details { get; set; } /// /// Gets or sets an array of inference request details. /// Note: this is an extension to the ACR specs to enable multiple input data types. /// [JsonPropertyName("inputs")] - public IList Inputs { get; set; } + public IList? Inputs { get; set; } } } diff --git a/src/Api/Rest/InferenceRequestResponse.cs b/src/Api/Rest/InferenceRequestResponse.cs index 3dc0f6a91..4dca001aa 100644 --- a/src/Api/Rest/InferenceRequestResponse.cs +++ b/src/Api/Rest/InferenceRequestResponse.cs @@ -25,6 +25,6 @@ public class InferenceRequestResponse /// /// Gets or sets the original request transaction ID. /// - public string TransactionId { get; set; } + public string TransactionId { get; set; } = default!; } } diff --git a/src/Api/Rest/InferenceStatusResponse.cs b/src/Api/Rest/InferenceStatusResponse.cs index 352020478..546cc8981 100644 --- a/src/Api/Rest/InferenceStatusResponse.cs +++ b/src/Api/Rest/InferenceStatusResponse.cs @@ -25,6 +25,6 @@ public class InferenceStatusResponse /// /// Gets or set the transaction ID of a request. /// - public string TransactionId { get; set; } + public string TransactionId { get; set; } = default!; } } diff --git a/src/Api/Rest/InputConnectionDetails.cs b/src/Api/Rest/InputConnectionDetails.cs index e1eb37ba3..d8bf363bd 100644 --- a/src/Api/Rest/InputConnectionDetails.cs +++ b/src/Api/Rest/InputConnectionDetails.cs @@ -30,13 +30,13 @@ public class InputConnectionDetails : DicomWebConnectionDetails /// Name is also used as the job name. /// [JsonPropertyName("name")] - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the MONAI Application name or ID. Used when /// is . /// [JsonPropertyName("id")] - public string Id { get; set; } + public string? Id { get; set; } } } diff --git a/src/Api/Rest/RequestInputDataResource.cs b/src/Api/Rest/RequestInputDataResource.cs index 55aa5152a..25d2d9aa9 100644 --- a/src/Api/Rest/RequestInputDataResource.cs +++ b/src/Api/Rest/RequestInputDataResource.cs @@ -60,6 +60,6 @@ public class RequestInputDataResource /// Gets or sets connection details of a data source. /// [JsonPropertyName("connectionDetails")] - public InputConnectionDetails ConnectionDetails { get; set; } + public InputConnectionDetails? ConnectionDetails { get; set; } } } diff --git a/src/Api/Rest/RequestOutputDataResource.cs b/src/Api/Rest/RequestOutputDataResource.cs index 78e7c6114..cb19bfd49 100644 --- a/src/Api/Rest/RequestOutputDataResource.cs +++ b/src/Api/Rest/RequestOutputDataResource.cs @@ -53,6 +53,6 @@ public class RequestOutputDataResource /// Gets or sets connection details of a data source. /// [JsonPropertyName("connectionDetails")] - public DicomWebConnectionDetails ConnectionDetails { get; set; } + public DicomWebConnectionDetails ConnectionDetails { get; set; } = default!; } } diff --git a/src/Api/Rest/RequestedInstance.cs b/src/Api/Rest/RequestedInstance.cs index 76bae16ed..ab79567f9 100644 --- a/src/Api/Rest/RequestedInstance.cs +++ b/src/Api/Rest/RequestedInstance.cs @@ -32,6 +32,6 @@ public class RequestedInstance /// Gets or sets the SOP Instance UID to be retrieved. /// [JsonPropertyName("SOPInstanceUID")] - public IList SopInstanceUid { get; set; } + public IList? SopInstanceUid { get; set; } } } diff --git a/src/Api/Rest/RequestedSeries.cs b/src/Api/Rest/RequestedSeries.cs index b98e4a446..5fa18513c 100644 --- a/src/Api/Rest/RequestedSeries.cs +++ b/src/Api/Rest/RequestedSeries.cs @@ -33,12 +33,12 @@ public class RequestedSeries /// Gets or sets the Series Instance UID to be retrieved. /// [JsonPropertyName("SeriesInstanceUID")] - public string SeriesInstanceUid { get; set; } + public string? SeriesInstanceUid { get; set; } /// /// Gets or sets a list of DICOM instances to be retrieved. /// [JsonPropertyName("instances")] - public IList Instances { get; set; } + public IList? Instances { get; set; } } } diff --git a/src/Api/Rest/RequestedStudy.cs b/src/Api/Rest/RequestedStudy.cs index 2db9ac8fc..adc0afb5d 100644 --- a/src/Api/Rest/RequestedStudy.cs +++ b/src/Api/Rest/RequestedStudy.cs @@ -55,12 +55,12 @@ public class RequestedStudy /// Gets or sets the Study Instance UID to be retrieved. /// [JsonPropertyName("StudyInstanceUID")] - public string StudyInstanceUid { get; set; } + public string? StudyInstanceUid { get; set; } /// /// Gets or sets a list of DICOM series to be retrieved. /// [JsonPropertyName("series")] - public IList Series { get; set; } + public IList? Series { get; set; } } } diff --git a/src/Api/SourceApplicationEntity.cs b/src/Api/SourceApplicationEntity.cs old mode 100644 new mode 100755 index 604bf2eae..360854848 --- a/src/Api/SourceApplicationEntity.cs +++ b/src/Api/SourceApplicationEntity.cs @@ -15,6 +15,8 @@ * limitations under the License. */ +using Monai.Deploy.InformaticsGateway.Api.Models; + namespace Monai.Deploy.InformaticsGateway.Api { /// @@ -31,5 +33,25 @@ namespace Monai.Deploy.InformaticsGateway.Api /// public class SourceApplicationEntity : BaseApplicationEntity { + public SourceApplicationEntity() : base() + { + SetDefaultValues(); + } + + /// + /// Gets or sets the AE Title (AET) used to identify itself in a DICOM association. + /// + public string AeTitle { get; set; } = default!; + + public override void SetDefaultValues() + { + if (string.IsNullOrWhiteSpace(Name)) + Name = AeTitle; + } + + public override string ToString() + { + return $"Name: {Name}/AET: {AeTitle}/Host: {HostIp}"; + } } } diff --git a/src/Api/Storage/DicomFileStorageMetadata.cs b/src/Api/Storage/DicomFileStorageMetadata.cs old mode 100644 new mode 100755 index d1a4d19d7..9a01fda52 --- a/src/Api/Storage/DicomFileStorageMetadata.cs +++ b/src/Api/Storage/DicomFileStorageMetadata.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ using System; using System.Text.Json.Serialization; using Ardalis.GuardClauses; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Api.Storage { @@ -31,38 +32,23 @@ public sealed record DicomFileStorageMetadata : FileStorageMetadata public static readonly string DicomContentType = "application/dicom"; public static readonly string DicomJsonContentType = System.Net.Mime.MediaTypeNames.Application.Json; - /// - /// The calling AE title of the DICOM instance. - /// For ACR, this is the Transaction ID of the original request. - /// Note: this value is same as - /// - [JsonIgnore] - public string CallingAeTitle { get => Source; } - - /// - /// The MONAI AE Title that received the DICOM instance. - /// For ACR request, this field is empty. - /// - [JsonPropertyName("calledAeTitle")] - public string CalledAeTitle { get; set; } - /// /// Gets or set the Study Instance UID of the DICOM instance. /// [JsonPropertyName("studyInstanceUid")] - public string StudyInstanceUid { get; init; } + public string StudyInstanceUid { get; set; } = default!; /// /// Gets or set the Series Instance UID of the DICOM instance. /// [JsonPropertyName("seriesInstanceUid")] - public string SeriesInstanceUid { get; init; } + public string SeriesInstanceUid { get; init; } = default!; /// /// Gets or set the SOP Instance UID of the DICOM instance. /// [JsonPropertyName("sopInstanceUid")] - public string SopInstanceUid { get; init; } + public string SopInstanceUid { get; init; } = default!; /// [JsonIgnore] @@ -70,13 +56,13 @@ public sealed record DicomFileStorageMetadata : FileStorageMetadata /// [JsonPropertyName("file")] - public override StorageObjectMetadata File { get; set; } + public override StorageObjectMetadata File { get; set; } = default!; /// /// Gets the storage object metadata for the JSON file associated with the DICOM instance. /// [JsonPropertyName("jsonFile")] - public StorageObjectMetadata JsonFile { get; set; } + public StorageObjectMetadata JsonFile { get; set; } = default!; [JsonIgnore] public override bool IsUploaded => base.IsUploaded && JsonFile.IsUploaded; @@ -95,18 +81,27 @@ public sealed record DicomFileStorageMetadata : FileStorageMetadata [JsonConstructor] public DicomFileStorageMetadata() { } - public DicomFileStorageMetadata(string associationId, string identifier, string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid) + public DicomFileStorageMetadata(string associationId, string identifier, string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid, DataService dataService, string callingAeTitle, string calledAeTitle) : base(associationId.ToString(), identifier) { - Guard.Against.NullOrWhiteSpace(associationId); - Guard.Against.NullOrWhiteSpace(identifier); - Guard.Against.NullOrWhiteSpace(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); - Guard.Against.NullOrWhiteSpace(sopInstanceUid); + Guard.Against.NullOrWhiteSpace(associationId, nameof(associationId)); + Guard.Against.NullOrWhiteSpace(identifier, nameof(identifier)); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); StudyInstanceUid = studyInstanceUid; SeriesInstanceUid = seriesInstanceUid; SopInstanceUid = sopInstanceUid; + SetupFilePaths(associationId); + + DataOrigin.DataService = dataService; + DataOrigin.Source = callingAeTitle; + DataOrigin.Destination = calledAeTitle; + } + + private void SetupFilePaths(string associationId) + { File = new StorageObjectMetadata(FileExtension) { TemporaryPath = string.Join(PathSeparator, associationId, DataTypeDirectoryName, $"{Guid.NewGuid()}{FileExtension}"), @@ -122,6 +117,39 @@ public DicomFileStorageMetadata(string associationId, string identifier, string }; } + public void SetupGivenFilePaths(string? DestinationFolder) + { + if (DestinationFolder is null) + { + return; + } + + if (DestinationFolder.EndsWith('/')) + { + DestinationFolder = DestinationFolder.Remove(DestinationFolder.Length - 1); + } + + File = new StorageObjectMetadata(FileExtension) + { + TemporaryPath = string.Join(PathSeparator, DestinationFolder, $"Temp{PathSeparator}{Guid.NewGuid()}{FileExtension}"), + UploadPath = string.Join(PathSeparator, DestinationFolder, $"{SopInstanceUid}{FileExtension}"), + ContentType = DicomContentType, + DestinationFolderOverride = true, + }; + + JsonFile = new StorageObjectMetadata(DicomJsonFileExtension) + { + TemporaryPath = $"{File.TemporaryPath}{DicomJsonFileExtension}", + UploadPath = $"{File.UploadPath}{DicomJsonFileExtension}", + ContentType = DicomJsonContentType, + DestinationFolderOverride = true, + }; + + //DestinationFolderNeil = DestinationFolder; + } + + public void SetStudyInstanceUid(string newStudyInstanceUid) => StudyInstanceUid = newStudyInstanceUid; + public override void SetFailed() { base.SetFailed(); diff --git a/src/Api/Storage/FhirFileStorageMetadata.cs b/src/Api/Storage/FhirFileStorageMetadata.cs index c98629cc6..a52ac0e8e 100644 --- a/src/Api/Storage/FhirFileStorageMetadata.cs +++ b/src/Api/Storage/FhirFileStorageMetadata.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ using System.Text.Json.Serialization; using Ardalis.GuardClauses; using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Api.Storage { @@ -28,26 +29,19 @@ public sealed record FhirFileStorageMetadata : FileStorageMetadata { public const string FhirSubDirectoryName = "ehr"; public const string JsonFilExtension = ".json"; - public const string XmlFilExtension = ".xml"; - - /// - /// The transaction ID of the original ACR request. - /// Note: this value is same as - /// - [JsonIgnore] - public string TransactionId { get => Source; } + public const string XmlFileExtension = ".xml"; /// /// Gets or set the FHIR resource type. /// [JsonPropertyName("resourceType")] - public string ResourceType { get; set; } + public string ResourceType { get; set; } = default!; /// /// Gets or set the FHIR resource ID. /// [JsonPropertyName("resourceId")] - public string ResourceId { get; set; } + public string ResourceId { get; set; } = default!; /// [JsonIgnore] @@ -55,7 +49,7 @@ public sealed record FhirFileStorageMetadata : FileStorageMetadata /// [JsonPropertyName("file")] - public override StorageObjectMetadata File { get; set; } + public override StorageObjectMetadata File { get; set; } = default!; /// /// DO NOT USE @@ -66,18 +60,21 @@ public sealed record FhirFileStorageMetadata : FileStorageMetadata [JsonConstructor] public FhirFileStorageMetadata() { } - public FhirFileStorageMetadata(string transactionId, string resourceType, string resourceId, FhirStorageFormat fhirFileFormat) + public FhirFileStorageMetadata(string transactionId, string resourceType, string resourceId, FhirStorageFormat fhirFileFormat, DataService dataType, string dataOrigin) : base(transactionId, $"{resourceType}{PathSeparator}{resourceId}") { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.NullOrWhiteSpace(resourceType); - Guard.Against.NullOrWhiteSpace(resourceId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.NullOrWhiteSpace(resourceType, nameof(resourceType)); + Guard.Against.NullOrWhiteSpace(resourceId, nameof(resourceId)); - Source = transactionId; ResourceType = resourceType; ResourceId = resourceId; - var fileExtension = fhirFileFormat == FhirStorageFormat.Json ? JsonFilExtension : XmlFilExtension; + DataOrigin.DataService = dataType; + DataOrigin.Source = dataOrigin; + DataOrigin.Destination = IpAddress(); + + var fileExtension = fhirFileFormat == FhirStorageFormat.Json ? JsonFilExtension : XmlFileExtension; File = new StorageObjectMetadata(fileExtension) { TemporaryPath = string.Join(PathSeparator, transactionId, DataTypeDirectoryName, $"{Guid.NewGuid()}{fileExtension}"), @@ -86,4 +83,4 @@ public FhirFileStorageMetadata(string transactionId, string resourceType, string }; } } -} +} \ No newline at end of file diff --git a/src/Api/Storage/FileStorageMetadata.cs b/src/Api/Storage/FileStorageMetadata.cs old mode 100644 new mode 100755 index 53c44a410..302612d07 --- a/src/Api/Storage/FileStorageMetadata.cs +++ b/src/Api/Storage/FileStorageMetadata.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,11 @@ using System; using System.Collections.Generic; +using System.Net; using System.Text.Json.Serialization; using Ardalis.GuardClauses; +using Microsoft.Extensions.Logging; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Api.Storage { @@ -52,7 +55,7 @@ public abstract record FileStorageMetadata /// Gets the unique (user-defined) ID of the file. /// [JsonPropertyName("id")] - public string Id { get; init; } + public string Id { get; init; } = default!; /// /// Gets the correlation ID of the file. @@ -60,26 +63,46 @@ public abstract record FileStorageMetadata /// For ACR retrieved DICOM/FHIR files: use the original transaction ID embedded in the request. /// [JsonPropertyName("correlationId")] - public string CorrelationId { get; init; } + public string CorrelationId { get; set; } = default!; /// - /// Gets or sets the source of the file. + /// Gets or sets the data origin of this file. /// - [JsonPropertyName("source")] - public string Source { get; set; } + [JsonPropertyName("dataOrigin"), JsonInclude] + public DataOrigin DataOrigin { get; private set; } = new(); /// /// Gets a list of workflows designated for the file. /// [JsonPropertyName("workflows"), JsonInclude] - public List Workflows { get; private set; } + public List Workflows { get; private set; } = default!; /// /// Gets or sets the DateTime that the file was received. /// /// [JsonPropertyName("dateReceived")] - public DateTime DateReceived { get; init; } + public DateTime DateReceived { get; init; } = default!; + + /// + /// Gets or sets the workflow instance ID for the workflow manager to resume a workflow. + /// + /// + [JsonPropertyName("WorkflowInstanceId")] + public string? WorkflowInstanceId { get; set; } + + /// + /// Gets or sets the task ID for the workflow manager to resume a workflow. + /// + /// + [JsonPropertyName("taskId")] + public string? TaskId { get; set; } + + /// + /// Gets or sets the PayloadId associated with this file. + /// + [JsonPropertyName("payloadId")] + public string? PayloadId { get; set; } /// /// DO NOT USE @@ -92,13 +115,14 @@ protected FileStorageMetadata() { } protected FileStorageMetadata(string correlationId, string identifier) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(identifier); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(identifier, nameof(identifier)); CorrelationId = correlationId; Id = identifier; DateReceived = DateTime.UtcNow; Workflows = new List(); + DataOrigin = new DataOrigin(); } /// @@ -107,7 +131,7 @@ protected FileStorageMetadata(string correlationId, string identifier) /// List of workflows. public void SetWorkflows(params string[] workflows) { - Guard.Against.NullOrEmpty(workflows); + Guard.Against.NullOrEmpty(workflows, nameof(workflows)); Workflows.AddRange(workflows); } @@ -116,5 +140,26 @@ public virtual void SetFailed() { File.SetFailed(); } + + public void ChangeCorrelationId(ILogger logger, string correlationId) + { + logger.LogWarning($"Changing correlation ID from {CorrelationId} to {correlationId}."); + CorrelationId = correlationId; + } + + public static string IpAddress() + { + var entry = Dns.GetHostEntry(Dns.GetHostName()); + + foreach (var ip in entry.AddressList) + { + if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + return ip.ToString(); + } + } + + return "127.0.0.1"; + } } -} +} \ No newline at end of file diff --git a/src/Api/Storage/Hl7FileStorageMetadata.cs b/src/Api/Storage/Hl7FileStorageMetadata.cs old mode 100644 new mode 100755 index 606a65499..b4e33aed3 --- a/src/Api/Storage/Hl7FileStorageMetadata.cs +++ b/src/Api/Storage/Hl7FileStorageMetadata.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ using System; using System.Text.Json.Serialization; using Ardalis.GuardClauses; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Api.Storage { @@ -26,7 +27,7 @@ namespace Monai.Deploy.InformaticsGateway.Api.Storage public sealed record Hl7FileStorageMetadata : FileStorageMetadata { public const string Hl7SubDirectoryName = "ehr"; - public const string FileExtension = ".txt"; + public const string FileExtension = ".hl7"; /// [JsonIgnore] @@ -34,7 +35,7 @@ public sealed record Hl7FileStorageMetadata : FileStorageMetadata /// [JsonPropertyName("file")] - public override StorageObjectMetadata File { get; set; } + public override StorageObjectMetadata File { get; set; } = default!; /// /// DO NOT USE @@ -45,12 +46,15 @@ public sealed record Hl7FileStorageMetadata : FileStorageMetadata [JsonConstructor] public Hl7FileStorageMetadata() { } - public Hl7FileStorageMetadata(string connectionId) + public Hl7FileStorageMetadata(string connectionId, DataService dataType, string dataOrigin) : base(connectionId, Guid.NewGuid().ToString()) { - Guard.Against.NullOrWhiteSpace(connectionId); + Guard.Against.NullOrWhiteSpace(connectionId, nameof(connectionId)); - Source = connectionId; + DataOrigin.DataService = dataType; + DataOrigin.Source = dataOrigin; + DataOrigin.Destination = IpAddress(); + DataOrigin.ArtifactType = Messaging.Common.ArtifactType.HL7; File = new StorageObjectMetadata(FileExtension) { diff --git a/src/Api/MongoDBEntityBase.cs b/src/Api/Storage/MongoDBEntityBase.cs old mode 100644 new mode 100755 similarity index 95% rename from src/Api/MongoDBEntityBase.cs rename to src/Api/Storage/MongoDBEntityBase.cs index 41b206a6e..1d2a38443 --- a/src/Api/MongoDBEntityBase.cs +++ b/src/Api/Storage/MongoDBEntityBase.cs @@ -16,7 +16,7 @@ using System; -namespace Monai.Deploy.InformaticsGateway.Api +namespace Monai.Deploy.InformaticsGateway.Api.Storage { public abstract class MongoDBEntityBase { diff --git a/src/Api/Storage/Payload.cs b/src/Api/Storage/Payload.cs old mode 100644 new mode 100755 index 80a2ccbb1..cccb148ec --- a/src/Api/Storage/Payload.cs +++ b/src/Api/Storage/Payload.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Transactions; using Ardalis.GuardClauses; -using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Api.Storage { @@ -48,12 +47,14 @@ public enum PayloadState private readonly Stopwatch _lastReceived; private bool _disposedValue; - public Guid PayloadId { get; } + public Guid PayloadId { get; private set; } public uint Timeout { get; init; } public string Key { get; init; } + public string? MachineName { get; init; } + public DateTime DateTimeCreated { get; private set; } public int RetryCount { get; set; } @@ -64,38 +65,80 @@ public enum PayloadState public string CorrelationId { get; init; } + public string? WorkflowInstanceId { get; init; } + + public string? TaskId { get; init; } + + public DataOrigin DataTrigger { get; init; } + + public HashSet DataOrigins { get; init; } + public int Count { get => Files.Count; } public bool HasTimedOut { get => ElapsedTime().TotalSeconds >= Timeout; } public TimeSpan Elapsed - { get { return DateTime.UtcNow.Subtract(DateTimeCreated); } } + { + get { return DateTime.UtcNow.Subtract(DateTimeCreated); } + } - public string CallingAeTitle { get => Files.OfType().Select(p => p.CallingAeTitle).FirstOrDefault(); } + public int FilesUploaded { get => Files.Count(p => p.IsUploaded); } - public string CalledAeTitle { get => Files.OfType().Select(p => p.CalledAeTitle).FirstOrDefault(); } + public int FilesFailedToUpload { get => Files.Count(p => p.IsUploadFailed); } - public Payload(string key, string correlationId, uint timeout) - { - Guard.Against.NullOrWhiteSpace(key); + public Payload() { } + public Payload(string key, string correlationId, string? workflowInstanceId, string? taskId, DataOrigin dataTrigger, uint timeout) + { + Guard.Against.NullOrWhiteSpace(key, nameof(key)); Files = new List(); + DataOrigins = new HashSet(); _lastReceived = new Stopwatch(); CorrelationId = correlationId; + WorkflowInstanceId = workflowInstanceId; + TaskId = taskId; + MachineName = Environment.MachineName; DateTimeCreated = DateTime.UtcNow; PayloadId = Guid.NewGuid(); Key = key; State = PayloadState.Created; RetryCount = 0; Timeout = timeout; + DataTrigger = dataTrigger; + } + + public Payload(string key, string correlationId, string? workflowInstanceId, string? taskId, DataOrigin dataTrigger, uint timeout, string? payloadId) : + this(key, correlationId, workflowInstanceId, taskId, dataTrigger, timeout) + { + Guard.Against.NullOrWhiteSpace(key, nameof(key)); + + if (payloadId is null) + { + PayloadId = Guid.NewGuid(); + } + else + { + PayloadId = Guid.Parse(payloadId); + } } public void Add(FileStorageMetadata value) { - Guard.Against.Null(value); + Guard.Against.Null(value, nameof(value)); Files.Add(value); + + if (!DataTrigger.Equals(value.DataOrigin)) + { + DataOrigins.Add(value.DataOrigin); + } + + //if (string.IsNullOrWhiteSpace(value.DestinationFolderNeil) is false) + //{ + // DestinationFolder = value.DestinationFolderNeil; + //} + _lastReceived.Reset(); _lastReceived.Start(); } diff --git a/src/Api/Storage/StorageObjectMetadata.cs b/src/Api/Storage/StorageObjectMetadata.cs old mode 100644 new mode 100755 index 6b1b03bd6..074463f2e --- a/src/Api/Storage/StorageObjectMetadata.cs +++ b/src/Api/Storage/StorageObjectMetadata.cs @@ -28,13 +28,13 @@ public class StorageObjectMetadata /// Gets or sets the temporary path before file is assembled into a payload. /// [JsonPropertyName("temporaryPath")] - public string TemporaryPath { get; set; } + public string TemporaryPath { get; set; } = default!; /// /// Gets or sets the path the file is stored within the payload directory. /// [JsonPropertyName("uploadPath")] - public string UploadPath { get; set; } + public string UploadPath { get; set; } = default!; /// /// Gets the file extension. @@ -46,38 +46,38 @@ public class StorageObjectMetadata /// Gets or sets the content type of the file. /// [JsonPropertyName("contentType")] - public string ContentType { get; set; } + public string ContentType { get; set; } = default!; /// /// Gets or sets the data stream. /// [JsonIgnore] - public Stream Data { get; set; } + public Stream Data { get; set; } = default!; [JsonPropertyName("payloadBucketName"), JsonInclude] - public string PayloadBucketName { get; private set; } + public string PayloadBucketName { get; private set; } = default!; [JsonPropertyName("dateMoved"), JsonInclude] - public DateTime DateMoved { get; private set; } + public DateTime DateMoved { get; private set; } = default!; /// /// Gets or set the date time file was uploaded. /// [JsonPropertyName("dateUploaded")] - public DateTime? DateUploaded { get; set; } + public DateTime? DateUploaded { get; set; } = default!; /// /// Gets the temporary bucket used for storing the file. /// [JsonPropertyName("temporaryBucketName"), JsonInclude] - public string TemporaryBucketName { get; private set; } + public string TemporaryBucketName { get; private set; } = default!; /// /// Gets or sets whether the file is uploaded to the temporary bucket. /// /// [JsonPropertyName("isUploaded"), JsonInclude] - public bool IsUploaded { get; private set; } + public bool IsUploaded { get; private set; } = default!; /// /// Gets or sets whether upload failed. @@ -86,11 +86,14 @@ public class StorageObjectMetadata public bool IsUploadFailed { get; private set; } [JsonPropertyName("isMoveCompleted"), JsonInclude] - public bool IsMoveCompleted { get; private set; } + public bool IsMoveCompleted { get; private set; } = default!; + + [JsonPropertyName("destinationFolderOverride")] + public bool DestinationFolderOverride { get; set; } = false; public StorageObjectMetadata(string fileExtension) { - Guard.Against.NullOrWhiteSpace(fileExtension); + Guard.Against.NullOrWhiteSpace(fileExtension, nameof(fileExtension)); if (fileExtension[0] != '.') { @@ -103,20 +106,24 @@ public StorageObjectMetadata(string fileExtension) public string GetTempStoragPath(string rootPath) { - Guard.Against.NullOrWhiteSpace(rootPath); + Guard.Against.NullOrWhiteSpace(rootPath, nameof(rootPath)); return $"{rootPath}{FileStorageMetadata.PathSeparator}{TemporaryPath}"; } public string GetPayloadPath(Guid payloadId) { - Guard.Against.Null(payloadId); + Guard.Against.Null(payloadId, nameof(payloadId)); - return $"{payloadId}{FileStorageMetadata.PathSeparator}{UploadPath}"; + if (DestinationFolderOverride is false) + { + return $"{payloadId}{FileStorageMetadata.PathSeparator}{UploadPath}"; + } + return $"{UploadPath}"; } public void SetUploaded(string bucketName) { - Guard.Against.NullOrWhiteSpace(bucketName); + Guard.Against.NullOrWhiteSpace(bucketName, nameof(bucketName)); TemporaryBucketName = bucketName; DateUploaded = DateTime.UtcNow; @@ -129,14 +136,14 @@ public void SetUploaded(string bucketName) var filename = fileStream.Name; Data.Close(); Data.Dispose(); - Data = null; + Data = default!; System.IO.File.Delete(filename); } else // MemoryStream { Data.Close(); Data.Dispose(); - Data = null; + Data = default!; // When IG stores all received/downloaded data in-memory using MemoryStream, LOH grows tremendously and thus impacts the performance and // memory usage. The following makes sure LOH is compacted after the data is uploaded. @@ -155,7 +162,7 @@ public void SetFailed() public void SetMoved(string bucketName) { - Guard.Against.NullOrEmpty(bucketName); + Guard.Against.NullOrEmpty(bucketName, nameof(bucketName)); PayloadBucketName = bucketName; DateMoved = DateTime.UtcNow; diff --git a/src/Api/Test/DestinationApplicationEntityTest.cs b/src/Api/Test/DestinationApplicationEntityTest.cs old mode 100644 new mode 100755 index 9e8287842..ec1cfe9be --- a/src/Api/Test/DestinationApplicationEntityTest.cs +++ b/src/Api/Test/DestinationApplicationEntityTest.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Monai.Deploy.InformaticsGateway.Api.Models; using Xunit; namespace Monai.Deploy.InformaticsGateway.Api.Test @@ -21,7 +22,7 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test public class MonaiApplicationEntityTest { [Fact] - public void GivenAMonaiApplicationEntity_WhenNameIsNotSet_ExepectSetDefaultValuesToBeUsed() + public void GivenAMonaiApplicationEntity_WhenNameIsNotSet_ExpectSetDefaultValuesToBeUsed() { var entity = new MonaiApplicationEntity { @@ -41,7 +42,7 @@ public void GivenAMonaiApplicationEntity_WhenNameIsNotSet_ExepectSetDefaultValue } [Fact] - public void GivenAMonaiApplicationEntity_WhenNameIsSet_ExepectSetDefaultValuesToNotOverwrite() + public void GivenAMonaiApplicationEntity_WhenNameIsSet_ExpectSetDefaultValuesToNotOverwrite() { var entity = new MonaiApplicationEntity { diff --git a/src/Api/Test/HL7DestinationEntityTest.cs b/src/Api/Test/HL7DestinationEntityTest.cs new file mode 100755 index 000000000..1a3d1b027 --- /dev/null +++ b/src/Api/Test/HL7DestinationEntityTest.cs @@ -0,0 +1,43 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Api.Test +{ + public class HL7DestinationEntityTest + { + + + [Fact] + public void GivenAMonaiApplicationEntity_WhenNameIsSet_ExepectSetDefaultValuesToNotOverwrite() + { + var entity = new HL7DestinationEntity + { + Port = 1104, + HostIp = "IP", + Name = "Name" + }; + + entity.SetDefaultValues(); + + Assert.Equal("IP", entity.HostIp); + Assert.Equal("Name", entity.Name); + Assert.Equal(1104, entity.Port); + } + } +} diff --git a/src/Api/Test/Hl7ApplicationConfigEntityTest.cs b/src/Api/Test/Hl7ApplicationConfigEntityTest.cs new file mode 100644 index 000000000..ae1a1bc2c --- /dev/null +++ b/src/Api/Test/Hl7ApplicationConfigEntityTest.cs @@ -0,0 +1,190 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Api.Test +{ + public class Hl7ApplicationConfigEntityTest + { + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenSendingIdKeyIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair(string.Empty, "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "DataMappingValue" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.SendingId.Key)} is missing.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenSendingIdValueIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", string.Empty), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "DataMappingValue" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.SendingId.Value)} is missing.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataLinkKeyIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair(string.Empty, DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "DataMappingValue" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.DataLink.Key)} is missing.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary()) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.DataMapping)} is missing values.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingKeyIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { string.Empty, "DataMappingValue" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.DataMapping)} is missing a name at index 0.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingValueIsNotSet_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", string.Empty } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.DataMapping)} (DataMappingKey) @ index 0 is not a valid DICOM Tag.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingValueIsNotAValidDicomTag_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "DataMappingValue" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains("DataMapping.Value is not a valid DICOM Tag. Error parsing DICOM tag ['DataMappingValue']", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingValueIsAValidDicomTag_ExpectValidateToReturnNoErrors() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "0020,000D" } }) + }; + + var errors = entity.Validate(); + + Assert.Empty(errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenDataMappingValueIsEmpty_ExpectValidateToReturnError() + { + var entity = new Hl7ApplicationConfigEntity + { + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "" } }) + }; + + var errors = entity.Validate(); + + Assert.NotEmpty(errors); + Assert.Contains($"{nameof(entity.DataMapping)} (DataMappingKey) @ index 0 is not a valid DICOM Tag.", errors); + } + + [Fact] + public void GivenAHl7ApplicationConfigEntity_WhenToStringIsCalled_ExpectToStringToReturnExpectedValue() + { + var guid = Guid.NewGuid(); + var dt = DateTime.UtcNow; + var entity = new Hl7ApplicationConfigEntity + { + Id = guid, + DateTimeCreated = dt, + SendingId = new KeyValuePair("SendingIdKey", "SendingIdValue"), + DataLink = new KeyValuePair("DataLinkKey", DataLinkType.PatientId), + DataMapping = StringKeyValuePair.FromDictionary(new Dictionary { { "DataMappingKey", "0020,000D" } }) + }; + + var result = entity.ToString(); + + var expected = JsonConvert.SerializeObject(entity); + Assert.Equal(expected, result); + } + } +} diff --git a/src/Api/Test/Monai.Deploy.InformaticsGateway.Api.Test.csproj b/src/Api/Test/Monai.Deploy.InformaticsGateway.Api.Test.csproj index 4a04855a4..bd39b5b04 100644 --- a/src/Api/Test/Monai.Deploy.InformaticsGateway.Api.Test.csproj +++ b/src/Api/Test/Monai.Deploy.InformaticsGateway.Api.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Api.Test Apache-2.0 false true - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + \ No newline at end of file diff --git a/src/Api/Test/MonaiApplicationEntityTest.cs b/src/Api/Test/MonaiApplicationEntityTest.cs old mode 100644 new mode 100755 index d91dd1166..1712d2690 --- a/src/Api/Test/MonaiApplicationEntityTest.cs +++ b/src/Api/Test/MonaiApplicationEntityTest.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Monai.Deploy.InformaticsGateway.Api.Models; using Xunit; namespace Monai.Deploy.InformaticsGateway.Api.Test @@ -21,7 +22,7 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test public class DestinationApplicationEntityTest { [Fact] - public void GivenADestinationApplicationEntity_WhenNameIsNotSet_ExepectSetDefaultValuesToSetName() + public void GivenADestinationApplicationEntity_WhenNameIsNotSet_ExpectSetDefaultValuesToSetName() { var entity = new DestinationApplicationEntity { @@ -36,7 +37,7 @@ public void GivenADestinationApplicationEntity_WhenNameIsNotSet_ExepectSetDefaul } [Fact] - public void GivenADestinationApplicationEntity_WhenNameIsSet_ExepectSetDefaultValuesToNotSetName() + public void GivenADestinationApplicationEntity_WhenNameIsSet_ExpectSetDefaultValuesToNotSetName() { var entity = new DestinationApplicationEntity { diff --git a/src/Api/Test/BaseApplicationEntityTest.cs b/src/Api/Test/SourceBaseApplicationEntityTest.cs old mode 100644 new mode 100755 similarity index 85% rename from src/Api/Test/BaseApplicationEntityTest.cs rename to src/Api/Test/SourceBaseApplicationEntityTest.cs index ef9b14808..801fc9049 --- a/src/Api/Test/BaseApplicationEntityTest.cs +++ b/src/Api/Test/SourceBaseApplicationEntityTest.cs @@ -18,12 +18,12 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test { - public class BaseApplicationEntityTest + public class SourceBaseApplicationEntityTest { [Fact] - public void GivenABaseApplicationEntity_WhenNameIsNotSet_ExepectSetDefaultValuesToSetName() + public void GivenABaseApplicationEntity_WhenNameIsNotSet_ExpectSetDefaultValuesToSetName() { - var entity = new BaseApplicationEntity + var entity = new SourceApplicationEntity { AeTitle = "AET", HostIp = "IP" @@ -35,9 +35,9 @@ public void GivenABaseApplicationEntity_WhenNameIsNotSet_ExepectSetDefaultValues } [Fact] - public void GivenABaseApplicationEntity_WhenNameIsSet_ExepectSetDefaultValuesToNotSetName() + public void GivenABaseApplicationEntity_WhenNameIsSet_ExpectSetDefaultValuesToNotSetName() { - var entity = new BaseApplicationEntity + var entity = new SourceApplicationEntity { AeTitle = "AET", HostIp = "IP", diff --git a/src/Api/Test/Storage/DicomFileStorageMetadataTest.cs b/src/Api/Test/Storage/DicomFileStorageMetadataTest.cs old mode 100644 new mode 100755 index 460c272b7..836fd75c0 --- a/src/Api/Test/Storage/DicomFileStorageMetadataTest.cs +++ b/src/Api/Test/Storage/DicomFileStorageMetadataTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ using System; using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.Messaging.Events; using Xunit; namespace Monai.Deploy.InformaticsGateway.Api.Test @@ -29,16 +30,13 @@ public void GivenDicomFileStorageMetadata_ExpectToHaveAETitlesSet() var identifier = Guid.NewGuid().ToString(); var callingAeTitle = "calling"; var calledAeTitle = "called"; - var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop") - { - Source = callingAeTitle, - CalledAeTitle = calledAeTitle - }; + var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop", DataService.DIMSE, callingAeTitle, calledAeTitle); Assert.Equal(identifier, metadata.Id); Assert.Equal(correlationId, metadata.CorrelationId); - Assert.Equal(callingAeTitle, metadata.CallingAeTitle); - Assert.Equal(calledAeTitle, metadata.CalledAeTitle); + Assert.Equal(callingAeTitle, metadata.DataOrigin.Source); + Assert.Equal(calledAeTitle, metadata.DataOrigin.Destination); + Assert.Equal(DataService.DIMSE, metadata.DataOrigin.DataService); Assert.NotNull(metadata.Workflows); } @@ -50,11 +48,7 @@ public void GivenDicomFileStorageMetadata_ExpectToHaveCorrectFilePathsSet() var callingAeTitle = "calling"; var calledAeTitle = "called"; - var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop") - { - Source = callingAeTitle, - CalledAeTitle = calledAeTitle - }; + var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop", DataService.DIMSE, callingAeTitle, calledAeTitle); Assert.Equal($"{DicomFileStorageMetadata.DicomSubDirectoryName}/study/series/sop{DicomFileStorageMetadata.FileExtension}", metadata.File.UploadPath); Assert.StartsWith($"{correlationId}/{DicomFileStorageMetadata.DicomSubDirectoryName}/", metadata.File.TemporaryPath, StringComparison.OrdinalIgnoreCase); @@ -73,11 +67,7 @@ public void GivenDicomFileStorageMetadata_WhenSetFailedIsCalled_AllFilesAreSetTo var callingAeTitle = "calling"; var calledAeTitle = "called"; - var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop") - { - Source = callingAeTitle, - CalledAeTitle = calledAeTitle - }; + var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop", DataService.DIMSE, callingAeTitle, calledAeTitle); metadata.SetFailed(); @@ -94,14 +84,45 @@ public void GivenDicomFileStorageMetadata_WhenGetPayloadPathIsCalled_APayyloadPa var callingAeTitle = "calling"; var calledAeTitle = "called"; - var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop") - { - Source = callingAeTitle, - CalledAeTitle = calledAeTitle - }; + var metadata = new DicomFileStorageMetadata(correlationId, identifier, "study", "series", "sop", DataService.DIMSE, callingAeTitle, calledAeTitle); Assert.Equal($"{payloadId}/{metadata.File.UploadPath}", metadata.File.GetPayloadPath(payloadId)); Assert.Equal($"{payloadId}/{metadata.JsonFile.UploadPath}", metadata.JsonFile.GetPayloadPath(payloadId)); } + + + [Fact] + public void StudyInstanceUid_Set_ValidValue() + { + // Arrange + var metadata = new DicomFileStorageMetadata(); + + // Act + metadata.StudyInstanceUid = "12345"; + + // Assert + Assert.Equal("12345", metadata.StudyInstanceUid); + } + + [Fact] + public void SeriesInstanceUid_Set_ValidValue() + { + // Arrange + var metadata = new DicomFileStorageMetadata { SeriesInstanceUid = "67890" }; + + // Assert + Assert.Equal("67890", metadata.SeriesInstanceUid); + } + + [Fact] + public void SopInstanceUid_Set_ValidValue() + { + // Arrange + var metadata = new DicomFileStorageMetadata { SopInstanceUid = "ABCDE" }; + + // Assert + Assert.Equal("ABCDE", metadata.SopInstanceUid); + } + } } diff --git a/src/Api/Test/Storage/FhirFileStorageMetadataTest.cs b/src/Api/Test/Storage/FhirFileStorageMetadataTest.cs index 454002cc0..774cb0b69 100644 --- a/src/Api/Test/Storage/FhirFileStorageMetadataTest.cs +++ b/src/Api/Test/Storage/FhirFileStorageMetadataTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ using System; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.Messaging.Events; using Xunit; namespace Monai.Deploy.InformaticsGateway.Api.Test @@ -24,18 +25,18 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test public class FhirFileStorageMetadataTest { [Theory(DisplayName = "Shall return FHIR resource upload path")] - [InlineData(FhirStorageFormat.Xml, FhirFileStorageMetadata.XmlFilExtension)] + [InlineData(FhirStorageFormat.Xml, FhirFileStorageMetadata.XmlFileExtension)] [InlineData(FhirStorageFormat.Json, FhirFileStorageMetadata.JsonFilExtension)] public void GivenFhirFileStorageMetadataWithSpecifiedFormat_ExpectToHaveCorrectFileExtension(FhirStorageFormat fileFormat, string fileExtension) { var correlationId = Guid.NewGuid().ToString(); - var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", fileFormat) + var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", fileFormat, DataService.FHIR, correlationId) { ResourceId = "ID", ResourceType = "TYPE", }; - Assert.Equal(correlationId, metadata.Source); + Assert.Equal(correlationId, metadata.DataOrigin.Source); Assert.Equal( $"{FhirFileStorageMetadata.FhirSubDirectoryName}/{metadata.ResourceType}/{metadata.ResourceId}{fileExtension}", metadata.File.UploadPath); @@ -50,7 +51,7 @@ public void GivenFhirFileStorageMetadataWithSpecifiedFormat_ExpectToHaveCorrectF public void GivenFhirFileStorageMetadata_WhenSetFailedIsCalled_AllFilesAreSetToFailed() { var correlationId = Guid.NewGuid().ToString(); - var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", FhirStorageFormat.Xml) + var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", FhirStorageFormat.Xml, DataService.FHIR, "origin") { ResourceId = "ID", ResourceType = "TYPE", @@ -66,7 +67,7 @@ public void GivenFhirFileStorageMetadata_WhenGetPayloadPathIsCalled_APayyloadPat { var payloadId = Guid.NewGuid(); var correlationId = Guid.NewGuid().ToString(); - var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", FhirStorageFormat.Xml) + var metadata = new FhirFileStorageMetadata(correlationId, "TYPE", "ID", FhirStorageFormat.Xml, DataService.FHIR, "origin") { ResourceId = "ID", ResourceType = "TYPE", @@ -75,4 +76,4 @@ public void GivenFhirFileStorageMetadata_WhenGetPayloadPathIsCalled_APayyloadPat Assert.Equal($"{payloadId}/{metadata.File.UploadPath}", metadata.File.GetPayloadPath(payloadId)); } } -} +} \ No newline at end of file diff --git a/src/Api/Test/Storage/PayloadTest.cs b/src/Api/Test/Storage/PayloadTest.cs index 6133ad1fb..d46ef503c 100644 --- a/src/Api/Test/Storage/PayloadTest.cs +++ b/src/Api/Test/Storage/PayloadTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,37 +25,68 @@ namespace Monai.Deploy.InformaticsGateway.Api.Test { public class PayloadTest { - [RetryFact(DisplayName = "Payload shall be able to add new instance and reset timer")] - public async Task Payload_AddsNewInstance() + [RetryFact] + public async Task GivenStorageInfoObjects_WhenAddIsCalled_ShouldAddToPayloadAndResetTime() { - var payload = new Payload("key", Guid.NewGuid().ToString(), 1); - payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt")); - await Task.Delay(450).ConfigureAwait(false); + var payload = new Payload("key", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "souce" })); + await Task.Delay(450).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(payload.HasTimedOut); - payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file2", ".txt")); - await Task.Delay(450).ConfigureAwait(false); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file2", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "souce" })); + await Task.Delay(450).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(payload.HasTimedOut); Assert.Equal("key", payload.Key); } - [RetryFact(DisplayName = "Payload shall not reset timer")] - public async Task Payload_ShallNotResetTimer() + [RetryFact] + public async Task GivenOneStorageInfoObject_AfterAddIsCalled_ExpectTimerToSet() { - var payload = new Payload("key", Guid.NewGuid().ToString(), 1); - payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt")); - await Task.Delay(1001).ConfigureAwait(false); + var payload = new Payload("key", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "souce" })); + await Task.Delay(1001).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(payload.HasTimedOut); } - [RetryFact(DisplayName = "Payload shall dispose timer")] - public void Payload_ShallDisposeTimer() + [RetryFact] + public void GivenAPayload_WhenDIsposed_ExpecteTImeToBeDiposed() { - var payload = new Payload("key", Guid.NewGuid().ToString(), 1); - payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt")); + var payload = new Payload("key", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "souce" })); Assert.Single(payload.Files); payload.Dispose(); Assert.Empty(payload.Files); Assert.False(payload.HasTimedOut); } + + [RetryFact] + public void GivenMultipleStorageObjectsWithDifferentDataOrigins_WhenAddedToPayload_ShouldHaveCorrectTriggerAndOrigins() + { + var payload = new Payload("key", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.ACR, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.ACR, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DicomWeb, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DicomWeb, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.HL7, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.HL7, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.FHIR, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.FHIR, Destination = "dest", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest1", Source = "souce" })); + payload.Add(new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new Messaging.Events.DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "souce2" })); + + Assert.StrictEqual(payload.DataTrigger, payload.Files[0].DataOrigin); + + Assert.Collection(payload.DataOrigins, + item => item.Equals(payload.Files[1].DataOrigin), + item => item.Equals(payload.Files[3].DataOrigin), + item => item.Equals(payload.Files[5].DataOrigin), + item => item.Equals(payload.Files[7].DataOrigin), + item => item.Equals(payload.Files[8].DataOrigin), + item => item.Equals(payload.Files[9].DataOrigin)); + + payload.Dispose(); + Assert.Empty(payload.Files); + Assert.False(payload.HasTimedOut); + } } } diff --git a/src/Api/Test/packages.lock.json b/src/Api/Test/packages.lock.json index 0abd2bb19..38b3d46ad 100644 --- a/src/Api/Test/packages.lock.json +++ b/src/Api/Test/packages.lock.json @@ -1,99 +1,103 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "System.IO.Abstractions.TestingHelpers": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "tkXvQbsfOIfeoGso+WptCuouFLiWt3EU8s0D8poqIVz1BJOOszkPuFbFgP2HUTJ9bp5n1HH89eFHILo6Oz5XUw==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", "dependencies": { - "System.IO.Abstractions": "17.2.3" + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" } }, "xRetry": { "type": "Direct", - "requested": "[1.8.0, )", - "resolved": "1.8.0", - "contentHash": "H8KXWHBjQASwD4y/7L2j7j4KLmg8z4+mCV4atrhZvJVnCkVSKLkWe1lfKGmaCYkKt2dJnC4yH+tJXGqthSkGGg==", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", "dependencies": { "xunit.core": "[2.4.0, 3.0.0)" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -101,41 +105,37 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -143,41 +143,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -194,280 +205,109 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "runtime.native.System.Net.Http": { + "Polly": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "Polly.Core": "8.5.2" } }, - "runtime.native.System.Security.Cryptography.Apple": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -475,60 +315,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -537,686 +323,70 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.Compression.ZipFile": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions.TestingHelpers": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Threading.Timer": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1226,61 +396,57 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/Api/VirtualApplicationEntity.cs b/src/Api/VirtualApplicationEntity.cs new file mode 100755 index 000000000..4fd33ffd1 --- /dev/null +++ b/src/Api/VirtualApplicationEntity.cs @@ -0,0 +1,119 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Security.Claims; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Api +{ + /// + /// Virtual Application Entity (VAE). + /// + /// A VAE identifies a service or application similar to a DIMSE AE but is + /// designed to be used for DICOMWeb. + /// + /// For example, users can configure VAEs on DICOMWeb STOW-RS endpoints to enable + /// data input plug-ins, . This allows different plug-ins + /// to be associated with each VAE for data manipulation, etc... + /// + /// In addition, one can trigger a new workflow request with the workflows defined in + /// when studies are posted to a VAE-enabled STOW-RS endpoint. + /// + public class VirtualApplicationEntity : MongoDBEntityBase + { + /// + /// Gets or sets the name of a MONAI DICOM application entity. + /// This value must be unique. + /// + [Key, Column(Order = 0)] + public string Name { get; set; } = default!; + + /// + /// Gets or sets the virtual AE TItle which is used as the URL fragment in the DICOMWeb STOW-RS endpoint. + /// E.g. POST /dicomweb/vae/{aet}/studies where {aet} is the value from this property. + /// + public string VirtualAeTitle { get; set; } = default!; + + /// + /// Optional field to map AE to one or more workflows. + /// + public List Workflows { get; set; } = default!; + + /// + /// Optional list of data input plug-in type names to be executed by the . + /// + public List PlugInAssemblies { get; set; } = default!; + + /// + /// Gets or set the user who created the DICOM entity. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or set the most recent user who updated the DICOM entity. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or set the most recent date time the DICOM entity was updated. + /// + public DateTime? DateTimeUpdated { get; set; } + + public VirtualApplicationEntity() + { + SetDefaultValues(); + } + + public void SetDefaultValues() + { + if (string.IsNullOrWhiteSpace(Name)) + { + Name = VirtualAeTitle; + } + + Workflows ??= new List(); + + PlugInAssemblies ??= new List(); + } + + public override string ToString() + { + return $"Name: {Name}/AET: {VirtualAeTitle}"; + } + + public void SetAuthor(ClaimsPrincipal user, EditMode editMode) + { + if (editMode == EditMode.Update) + { + DateTimeUpdated = DateTime.UtcNow; + } + + if (editMode == EditMode.Create) + { + CreatedBy = user.Identity?.Name; + } + else if (editMode == EditMode.Update) + { + UpdatedBy = user.Identity?.Name; + } + } + } +} diff --git a/src/Api/packages.lock.json b/src/Api/packages.lock.json index 725b8e793..d84a78818 100644 --- a/src/Api/packages.lock.json +++ b/src/Api/packages.lock.json @@ -1,12 +1,31 @@ { "version": 1, "dependencies": { - "net6.0": { - "GitVersion.MsBuild": { + "net8.0": { + "fo-dicom": { + "type": "Direct", + "requested": "[5.2.1, )", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" + } + }, + "HL7-dotnetcore": { "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" + "requested": "[2.39.1, )", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Direct", @@ -16,108 +35,96 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "Monai.Deploy.Messaging": { "type": "Direct", - "requested": "[0.1.16, )", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "requested": "[2.0.4, )", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Direct", + "requested": "[2.0.4, )", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Direct", - "requested": "[0.2.10, )", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "requested": "[1.0.2, )", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, - "fo-dicom": { - "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", - "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Channels": "6.0.0" - } - }, - "JetBrains.Annotations": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.Extensions.Configuration": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -125,41 +132,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -176,60 +194,67 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.NETCore.Platforms": { + "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" + } }, - "Microsoft.Toolkit.HighPerformance": { + "Newtonsoft.Json": { "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Monai.Deploy.Storage.S3Policy": { + "Polly": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Polly.Core": "8.5.2" } }, - "Newtonsoft.Json": { + "Polly.Core": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -240,8 +265,17 @@ }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -250,39 +284,42 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/AssemblyInfo.cs b/src/AssemblyInfo.cs new file mode 100644 index 000000000..1ae63f2a5 --- /dev/null +++ b/src/AssemblyInfo.cs @@ -0,0 +1,21 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; + +[assembly: AssemblyVersion("0.0.0.0")] +[assembly: AssemblyFileVersion("0.0.0.0")] +[assembly: AssemblyInformationalVersion("0.0.0")] diff --git a/src/CLI/Commands/AetCommand.cs b/src/CLI/Commands/AetCommand.cs old mode 100644 new mode 100755 index dcaa01b65..1cbed275d --- a/src/CLI/Commands/AetCommand.cs +++ b/src/CLI/Commands/AetCommand.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.CLI.Services; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.Common; @@ -40,8 +40,10 @@ public AetCommand() : base("aet", "Configure SCP Application Entities") AddAlias("aetitle"); SetupAddAetCommand(); + SetupEditAetCommand(); SetupRemoveAetCommand(); SetupListAetCommand(); + SetupPlugInsCommand(); } private void SetupListAetCommand() @@ -96,13 +98,66 @@ private void SetupAddAetCommand() IsRequired = false, }; addCommand.AddOption(allowedSopsOption); + var plugins = new Option>(new string[] { "-p", "--plugins" }, description: "A space separated list of fully qualified type names of the plug-ins (surround each plug-in with double quotes)") + { + AllowMultipleArgumentsPerToken = true, + IsRequired = false, + }; + addCommand.AddOption(plugins); addCommand.Handler = CommandHandler.Create(AddAeTitlehandlerAsync); } + private void SetupEditAetCommand() + { + var addCommand = new Command("update", "Update a SCP Application Entities"); + AddCommand(addCommand); + + var nameOption = new Option(new string[] { "-n", "--name" }, "Name of the SCP Application Entity") { IsRequired = false }; + addCommand.AddOption(nameOption); + var groupingOption = new Option(new string[] { "-g", "--grouping" }, getDefaultValue: () => "0020,000D", "DICOM tag used to group instances") { IsRequired = false }; + addCommand.AddOption(groupingOption); + var timeoutOption = new Option(new string[] { "-t", "--timeout" }, getDefaultValue: () => 5, "Timeout, in seconds, to wait for instances") { IsRequired = false }; + addCommand.AddOption(timeoutOption); + var workflowsOption = new Option>(new string[] { "-w", "--workflows" }, description: "A space separated list of workflow names or IDs to be associated with the SCP AE Title") + { + AllowMultipleArgumentsPerToken = true, + IsRequired = false, + }; + addCommand.AddOption(workflowsOption); + var ignoredSopsOption = new Option>(new string[] { "-i", "--ignored-sop-classes" }, description: "A space separated list of SOP Class UIDs to be ignored") + { + AllowMultipleArgumentsPerToken = true, + IsRequired = false, + }; + addCommand.AddOption(ignoredSopsOption); + var allowedSopsOption = new Option>(new string[] { "-s", "--allowed-sop-classes" }, description: "A space separated list of SOP Class UIDs to be accepted") + { + AllowMultipleArgumentsPerToken = true, + IsRequired = false, + }; + addCommand.AddOption(allowedSopsOption); + var plugins = new Option>(new string[] { "-p", "--plugins" }, description: "A space separated list of fully qualified type names of the plug-ins (surround each plug-in with double quotes)") + { + AllowMultipleArgumentsPerToken = true, + IsRequired = false, + }; + addCommand.AddOption(plugins); + + addCommand.Handler = CommandHandler.Create(EditAeTitleHandlerAsync); + } + + private void SetupPlugInsCommand() + { + var pluginsCommand = new Command("plugins", "List all available plug-ins for SCP Application Entities"); + AddCommand(pluginsCommand); + + pluginsCommand.Handler = CommandHandler.Create(ListPlugInsHandlerAsync); + } + private async Task ListAeTitlehandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); @@ -118,7 +173,7 @@ private async Task ListAeTitlehandlerAsync(IHost host, bool verbose, Cancel Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable."); - IReadOnlyList items = null; + IReadOnlyList? items = null; try { CheckConfiguration(configService); @@ -170,8 +225,8 @@ private async Task ListAeTitlehandlerAsync(IHost host, bool verbose, Cancel private async Task RemoveAeTitlehandlerAsync(string name, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(name); - Guard.Against.Null(host); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -206,8 +261,8 @@ private async Task RemoveAeTitlehandlerAsync(string name, IHost host, bool private async Task AddAeTitlehandlerAsync(MonaiApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -241,7 +296,10 @@ private async Task AddAeTitlehandlerAsync(MonaiApplicationEntity entity, IH if (result.AllowedSopClasses.Any()) { logger.MonaiAeAllowedSops(string.Join(',', result.AllowedSopClasses)); - logger.AcceptedSopClassesWarning(); + } + if (result.PlugInAssemblies.Any()) + { + logger.MonaiAePlugIns(string.Join(',', result.PlugInAssemblies)); } } catch (ConfigurationException ex) @@ -256,5 +314,125 @@ private async Task AddAeTitlehandlerAsync(MonaiApplicationEntity entity, IH } return ExitCodes.Success; } + + private async Task EditAeTitleHandlerAsync(MonaiApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationToken) + { + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); + + LogVerbose(verbose, host, "Configuring services..."); + var configService = host.Services.GetRequiredService(); + var client = host.Services.GetRequiredService(); + var logger = CreateLogger(host); + + Guard.Against.Null(logger, nameof(logger), "Logger is unavailable."); + Guard.Against.Null(configService, nameof(configService), "Configuration service is unavailable."); + Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); + + try + { + CheckConfiguration(configService); + client.ConfigureServiceUris(configService.Configurations.InformaticsGatewayServerUri); + + LogVerbose(verbose, host, $"Connecting to {Strings.ApplicationName} at {configService.Configurations.InformaticsGatewayServerEndpoint}..."); + LogVerbose(verbose, host, $"Updating SCP AE Title {entity.AeTitle}..."); + var result = await client.MonaiScpAeTitle.Update(entity, cancellationToken).ConfigureAwait(false); + + logger.MonaiAeTitleUpdated(result.Name, result.AeTitle, result.Grouping, result.Timeout); + if (result.Workflows.Any()) + { + logger.MonaiAeWorkflows(string.Join(',', result.Workflows)); + logger.WorkflowWarning(); + } + if (result.IgnoredSopClasses.Any()) + { + logger.MonaiAeIgnoredSops(string.Join(',', result.IgnoredSopClasses)); + logger.IgnoredSopClassesWarning(); + } + if (result.AllowedSopClasses.Any()) + { + logger.MonaiAeAllowedSops(string.Join(',', result.AllowedSopClasses)); + logger.AcceptedSopClassesWarning(); + } + if (result.PlugInAssemblies.Any()) + { + logger.MonaiAePlugIns(string.Join(',', result.PlugInAssemblies)); + } + } + catch (ConfigurationException ex) + { + logger.ConfigurationException(ex.Message); + return ExitCodes.Config_NotConfigured; + } + catch (Exception ex) + { + logger.ErrorUpdatingMonaiApplicationEntity(entity.AeTitle, ex.Message); + return ExitCodes.MonaiScp_ErrorUpdate; + } + return ExitCodes.Success; + } + + private async Task ListPlugInsHandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken) + { + Guard.Against.Null(host, nameof(host)); + + LogVerbose(verbose, host, "Configuring services..."); + + var console = host.Services.GetRequiredService(); + var configService = host.Services.GetRequiredService(); + var client = host.Services.GetRequiredService(); + var consoleRegion = host.Services.GetRequiredService(); + var logger = CreateLogger(host); + + Guard.Against.Null(logger, nameof(logger), "Logger is unavailable."); + Guard.Against.Null(console, nameof(console), "Console service is unavailable."); + Guard.Against.Null(configService, nameof(configService), "Configuration service is unavailable."); + Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); + Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable."); + + IDictionary? items = null; + try + { + CheckConfiguration(configService); + client.ConfigureServiceUris(configService.Configurations.InformaticsGatewayServerUri); + LogVerbose(verbose, host, $"Connecting to {Strings.ApplicationName} at {configService.Configurations.InformaticsGatewayServerEndpoint}..."); + LogVerbose(verbose, host, $"Retrieving MONAI SCP AE Titles..."); + items = await client.MonaiScpAeTitle.PlugIns(cancellationToken).ConfigureAwait(false); + } + catch (ConfigurationException ex) + { + logger.ConfigurationException(ex.Message); + return ExitCodes.Config_NotConfigured; + } + catch (Exception ex) + { + logger.ErrorListingDataInputPlugIns(ex.Message); + return ExitCodes.MonaiScp_ErrorPlugIns; + } + + if (items.IsNullOrEmpty()) + { + logger.NoAeTitlesFound(); + } + else + { + if (console is ITerminal terminal) + { + terminal.Clear(); + } + var consoleRenderer = new ConsoleRenderer(console); + + var table = new TableView> + { + Items = items.Select(x => new KeyValuePair(x.Key, x.Value)).ToList() + }; + table.AddColumn(p => p.Key, new ContentView("Name".Underline())); + table.AddColumn(p => p.Value, new ContentView("Assembly Name".Underline())); + table.Render(consoleRenderer, consoleRegion.GetDefaultConsoleRegion()); + + logger.ListedNItems(items.Count); + } + return ExitCodes.Success; + } } } diff --git a/src/CLI/Commands/CommandBase.cs b/src/CLI/Commands/CommandBase.cs index 8748a37c0..3521394da 100644 --- a/src/CLI/Commands/CommandBase.cs +++ b/src/CLI/Commands/CommandBase.cs @@ -31,9 +31,9 @@ public CommandBase(string name, string description) : base(name, description) { } - protected static ILogger CreateLogger(IHost host) + protected static ILogger? CreateLogger(IHost host) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); var loggerFactory = host.Services.GetService(); return loggerFactory?.CreateLogger(); @@ -41,8 +41,8 @@ protected static ILogger CreateLogger(IHost host) protected static void LogVerbose(bool verbose, IHost host, string message) { - Guard.Against.Null(host); - Guard.Against.NullOrWhiteSpace(message); + Guard.Against.Null(host, nameof(host)); + Guard.Against.NullOrWhiteSpace(message, nameof(message)); if (verbose) { @@ -62,7 +62,7 @@ protected static void LogVerbose(bool verbose, IHost host, string message) protected static void AddConfirmationOption(Command command) { - Guard.Against.Null(command); + Guard.Against.Null(command, nameof(command)); var confirmationOption = new Option(new[] { "-y", "--yes" }, "Automatic yes to prompts"); command.AddOption(confirmationOption); @@ -70,7 +70,7 @@ protected static void AddConfirmationOption(Command command) protected static void CheckConfiguration(IConfigurationService configService) { - Guard.Against.Null(configService); + Guard.Against.Null(configService, nameof(configService)); if (!configService.IsInitialized) { diff --git a/src/CLI/Commands/ConfigCommand.cs b/src/CLI/Commands/ConfigCommand.cs index 51323b372..f9305ab28 100644 --- a/src/CLI/Commands/ConfigCommand.cs +++ b/src/CLI/Commands/ConfigCommand.cs @@ -84,7 +84,7 @@ private void SetupShowConfigCommand() private int ShowConfigurationHandler(IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var logger = CreateLogger(host); @@ -94,19 +94,19 @@ private int ShowConfigurationHandler(IHost host, bool verbose, CancellationToken try { CheckConfiguration(configService); - logger.ConfigInformaticsGatewayApiEndpoint(configService.Configurations.InformaticsGatewayServerEndpoint); - logger.ConfigDicomScpPort(configService.Configurations.DicomListeningPort); - logger.ConfigContainerRunner(configService.Configurations.Runner); - logger.ConfigHostInfo(configService.Configurations.HostDatabaseStorageMount, configService.Configurations.HostDataStorageMount, configService.Configurations.HostLogsStorageMount); + logger?.ConfigInformaticsGatewayApiEndpoint(configService.Configurations.InformaticsGatewayServerEndpoint); + logger?.ConfigDicomScpPort(configService.Configurations.DicomListeningPort); + logger?.ConfigContainerRunner(configService.Configurations.Runner); + logger?.ConfigHostInfo(configService.Configurations.HostDatabaseStorageMount, configService.Configurations.HostDataStorageMount, configService.Configurations.HostLogsStorageMount); } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) { - logger.CriticalException(ex.Message); + logger?.CriticalException(ex.Message); return ExitCodes.Config_ErrorShowing; } return ExitCodes.Success; @@ -114,8 +114,8 @@ private int ShowConfigurationHandler(IHost host, bool verbose, CancellationToken private static int ConfigUpdateHandler(IHost host, Action updater) { - Guard.Against.Null(host); - Guard.Against.Null(updater); + Guard.Against.Null(host, nameof(host)); + Guard.Against.Null(updater, nameof(updater)); var logger = CreateLogger(host); var config = host.Services.GetRequiredService(); @@ -126,16 +126,16 @@ private static int ConfigUpdateHandler(IHost host, Action { CheckConfiguration(config); updater(config); - logger.ConfigurationUpdated(); + logger?.ConfigurationUpdated(); } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) { - logger.CriticalException(ex.Message); + logger?.CriticalException(ex.Message); return ExitCodes.Config_ErrorSaving; } return ExitCodes.Success; @@ -143,7 +143,7 @@ private static int ConfigUpdateHandler(IHost host, Action private async Task InitHandlerAsync(IHost host, bool verbose, bool yes, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); var logger = CreateLogger(host); var configService = host.Services.GetRequiredService(); @@ -153,7 +153,7 @@ private async Task InitHandlerAsync(IHost host, bool verbose, bool yes, Can if (!yes && configService.IsConfigExists && !confirmation.ShowConfirmationPrompt($"Existing application configuration file already exists. Do you want to overwrite it?")) { - logger.ActionCancelled(); + logger?.ActionCancelled(); return ExitCodes.Stop_Cancelled; } @@ -163,7 +163,7 @@ private async Task InitHandlerAsync(IHost host, bool verbose, bool yes, Can } catch (Exception ex) { - logger.CriticalException(ex.Message); + logger?.CriticalException(ex.Message); return ExitCodes.Config_ErrorInitializing; } return ExitCodes.Success; diff --git a/src/CLI/Commands/ConfigurationException.cs b/src/CLI/Commands/ConfigurationException.cs index 5ab8c9539..29c177ea1 100644 --- a/src/CLI/Commands/ConfigurationException.cs +++ b/src/CLI/Commands/ConfigurationException.cs @@ -15,11 +15,9 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.CLI { - [Serializable] public class ConfigurationException : Exception { private ConfigurationException() @@ -33,9 +31,5 @@ public ConfigurationException(string message) : base(message) public ConfigurationException(string message, Exception innerException) : base(message, innerException) { } - - protected ConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/CLI/Commands/DestinationCommand.cs b/src/CLI/Commands/DestinationCommand.cs old mode 100644 new mode 100755 index 6e8239a87..4480cd8a4 --- a/src/CLI/Commands/DestinationCommand.cs +++ b/src/CLI/Commands/DestinationCommand.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.CLI.Services; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.Common; @@ -45,6 +45,7 @@ public DestinationCommand() : base("dst", "Configure DICOM destinations") SetupRemoveDestinationCommand(); SetupListDestinationCommand(); SetupCEchoCommand(); + SetupPlugInsCommand(); } private void SetupCEchoCommand() @@ -114,10 +115,18 @@ private void SetupAddDestinationCommand() addCommand.Handler = CommandHandler.Create(AddDestinationHandlerAsync); } + private void SetupPlugInsCommand() + { + var pluginsCommand = new Command("plugins", "List all available plug-ins for DICOM destinations"); + AddCommand(pluginsCommand); + + pluginsCommand.Handler = CommandHandler.Create(ListPlugInsHandlerAsync); + } + private async Task ListDestinationHandlerAsync(DestinationApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); @@ -133,7 +142,7 @@ private async Task ListDestinationHandlerAsync(DestinationApplicationEntity Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable."); - IReadOnlyList items = null; + IReadOnlyList? items = null; try { CheckConfiguration(configService); @@ -144,12 +153,12 @@ private async Task ListDestinationHandlerAsync(DestinationApplicationEntity } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) { - logger.ErrorListingDicomDestinations(ex.Message); + logger?.ErrorListingDicomDestinations(ex.Message); return ExitCodes.DestinationAe_ErrorList; } @@ -182,8 +191,8 @@ private async Task ListDestinationHandlerAsync(DestinationApplicationEntity private async Task CEchoDestinationHandlerAsync(string name, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(name); - Guard.Against.Null(host); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -205,7 +214,7 @@ private async Task CEchoDestinationHandlerAsync(string name, IHost host, bo } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -218,8 +227,8 @@ private async Task CEchoDestinationHandlerAsync(string name, IHost host, bo private async Task RemoveDestinationHandlerAsync(string name, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(name); - Guard.Against.Null(host); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -241,7 +250,7 @@ private async Task RemoveDestinationHandlerAsync(string name, IHost host, b } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -254,8 +263,8 @@ private async Task RemoveDestinationHandlerAsync(string name, IHost host, b private async Task EditDestinationHandlerAsync(DestinationApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -279,7 +288,7 @@ private async Task EditDestinationHandlerAsync(DestinationApplicationEntity } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -292,8 +301,8 @@ private async Task EditDestinationHandlerAsync(DestinationApplicationEntity private async Task AddDestinationHandlerAsync(DestinationApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -316,7 +325,7 @@ private async Task AddDestinationHandlerAsync(DestinationApplicationEntity } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -326,5 +335,68 @@ private async Task AddDestinationHandlerAsync(DestinationApplicationEntity } return ExitCodes.Success; } + + private async Task ListPlugInsHandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken) + { + Guard.Against.Null(host, nameof(host)); + + LogVerbose(verbose, host, "Configuring services..."); + + var console = host.Services.GetRequiredService(); + var configService = host.Services.GetRequiredService(); + var client = host.Services.GetRequiredService(); + var consoleRegion = host.Services.GetRequiredService(); + var logger = CreateLogger(host); + + Guard.Against.Null(logger, nameof(logger), "Logger is unavailable."); + Guard.Against.Null(console, nameof(console), "Console service is unavailable."); + Guard.Against.Null(configService, nameof(configService), "Configuration service is unavailable."); + Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); + Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable."); + + IDictionary? items = null; + try + { + CheckConfiguration(configService); + client.ConfigureServiceUris(configService.Configurations.InformaticsGatewayServerUri); + LogVerbose(verbose, host, $"Connecting to {Strings.ApplicationName} at {configService.Configurations.InformaticsGatewayServerEndpoint}..."); + LogVerbose(verbose, host, $"Retrieving MONAI SCP AE Titles..."); + items = await client.DicomDestinations.PlugIns(cancellationToken).ConfigureAwait(false); + } + catch (ConfigurationException ex) + { + logger?.ConfigurationException(ex.Message); + return ExitCodes.Config_NotConfigured; + } + catch (Exception ex) + { + logger?.ErrorListingDataOutputPlugIns(ex.Message); + return ExitCodes.DestinationAe_ErrorPlugIns; + } + + if (items.IsNullOrEmpty()) + { + logger.NoAeTitlesFound(); + } + else + { + if (console is ITerminal terminal) + { + terminal.Clear(); + } + var consoleRenderer = new ConsoleRenderer(console); + + var table = new TableView> + { + Items = items.Select(x => new KeyValuePair(x.Key, x.Value)).ToList() + }; + table.AddColumn(p => p.Key, new ContentView("Name".Underline())); + table.AddColumn(p => p.Value, new ContentView("Assembly Name".Underline())); + table.Render(consoleRenderer, consoleRegion.GetDefaultConsoleRegion()); + + logger.ListedNItems(items.Count); + } + return ExitCodes.Success; + } } } diff --git a/src/CLI/Commands/RestartCommand.cs b/src/CLI/Commands/RestartCommand.cs index c9d06653d..ba9cc479f 100644 --- a/src/CLI/Commands/RestartCommand.cs +++ b/src/CLI/Commands/RestartCommand.cs @@ -35,7 +35,7 @@ public RestartCommand() : base("restart", $"Restart the {Strings.ApplicationName private async Task RestartCommandHandler(IHost host, bool yes, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); var service = host.Services.GetRequiredService(); var confirmation = host.Services.GetRequiredService(); diff --git a/src/CLI/Commands/SourceCommand.cs b/src/CLI/Commands/SourceCommand.cs index cc1670efd..47ae0e68d 100644 --- a/src/CLI/Commands/SourceCommand.cs +++ b/src/CLI/Commands/SourceCommand.cs @@ -45,7 +45,6 @@ public SourceCommand() : base("src", "Configure DICOM sources") SetupListSourceCommand(); } - private void SetupListSourceCommand() { var listCommand = new Command("ls", "List all DICOM sources"); @@ -81,6 +80,7 @@ private void SetupAddSourceCommand() addCommand.Handler = CommandHandler.Create(AddSourceHandlerAsync); } + private void SetupUpdateSourceCommand() { var addCommand = new Command("update", "Update a new DICOM source"); @@ -98,8 +98,8 @@ private void SetupUpdateSourceCommand() private async Task ListSourceHandlerAsync(SourceApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationTokena) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); @@ -115,7 +115,7 @@ private async Task ListSourceHandlerAsync(SourceApplicationEntity entity, I Guard.Against.Null(client, nameof(client), $"{Strings.ApplicationName} client is unavailable."); Guard.Against.Null(consoleRegion, nameof(consoleRegion), "Console region is unavailable."); - IReadOnlyList items = null; + IReadOnlyList? items = null; try { CheckConfiguration(configService); @@ -126,12 +126,12 @@ private async Task ListSourceHandlerAsync(SourceApplicationEntity entity, I } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) { - logger.ErrorListingDicomSources(ex.Message); + logger?.ErrorListingDicomSources(ex.Message); return ExitCodes.SourceAe_ErrorList; } @@ -163,8 +163,8 @@ private async Task ListSourceHandlerAsync(SourceApplicationEntity entity, I private async Task RemoveSourceHandlerAsync(string name, IHost host, bool verbose, CancellationToken cancellationTokena) { - Guard.Against.NullOrWhiteSpace(name); - Guard.Against.Null(host); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -186,7 +186,7 @@ private async Task RemoveSourceHandlerAsync(string name, IHost host, bool v } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -199,8 +199,8 @@ private async Task RemoveSourceHandlerAsync(string name, IHost host, bool v private async Task AddSourceHandlerAsync(SourceApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationTokena) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -223,7 +223,7 @@ private async Task AddSourceHandlerAsync(SourceApplicationEntity entity, IH } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) @@ -233,10 +233,11 @@ private async Task AddSourceHandlerAsync(SourceApplicationEntity entity, IH } return ExitCodes.Success; } + private async Task UpdateSourceHandlerAsync(SourceApplicationEntity entity, IHost host, bool verbose, CancellationToken cancellationTokena) { - Guard.Against.Null(entity); - Guard.Against.Null(host); + Guard.Against.Null(entity, nameof(entity)); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); var configService = host.Services.GetRequiredService(); @@ -259,7 +260,7 @@ private async Task UpdateSourceHandlerAsync(SourceApplicationEntity entity, } catch (ConfigurationException ex) { - logger.ConfigurationException(ex.Message); + logger?.ConfigurationException(ex.Message); return ExitCodes.Config_NotConfigured; } catch (Exception ex) diff --git a/src/CLI/Commands/StartCommand.cs b/src/CLI/Commands/StartCommand.cs index 3ceb4c574..c59545b6a 100644 --- a/src/CLI/Commands/StartCommand.cs +++ b/src/CLI/Commands/StartCommand.cs @@ -35,7 +35,7 @@ public StartCommand() : base("start", $"Start the {Strings.ApplicationName} serv private async Task StartCommandHandler(IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); var service = host.Services.GetRequiredService(); var confirmation = host.Services.GetRequiredService(); diff --git a/src/CLI/Commands/StatusCommand.cs b/src/CLI/Commands/StatusCommand.cs index 23cf0fb0a..b64573b71 100644 --- a/src/CLI/Commands/StatusCommand.cs +++ b/src/CLI/Commands/StatusCommand.cs @@ -36,7 +36,7 @@ public StatusCommand() : base("status", $"{Strings.ApplicationName} service stat private async Task StatusCommandHandlerAsync(IHost host, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); LogVerbose(verbose, host, "Configuring services..."); diff --git a/src/CLI/Commands/StopCommand.cs b/src/CLI/Commands/StopCommand.cs index 51a031ca1..2b4eef74f 100644 --- a/src/CLI/Commands/StopCommand.cs +++ b/src/CLI/Commands/StopCommand.cs @@ -35,7 +35,7 @@ public StopCommand() : base("stop", $"Stop the {Strings.ApplicationName} service private async Task StopCommandHandler(IHost host, bool yes, bool verbose, CancellationToken cancellationToken) { - Guard.Against.Null(host); + Guard.Against.Null(host, nameof(host)); var service = host.Services.GetRequiredService(); var confirmation = host.Services.GetRequiredService(); diff --git a/src/CLI/ControlException.cs b/src/CLI/ControlException.cs index 35f9152da..51782bfbe 100644 --- a/src/CLI/ControlException.cs +++ b/src/CLI/ControlException.cs @@ -15,12 +15,9 @@ */ using System; -using System.Runtime.Serialization; -using Ardalis.GuardClauses; namespace Monai.Deploy.InformaticsGateway.CLI { - [Serializable] public class ControlException : Exception { public int ErrorCode { get; } @@ -41,19 +38,5 @@ public ControlException(int errorCode, string message) : base(message) { ErrorCode = errorCode; } - - protected ControlException(SerializationInfo info, StreamingContext context) : base(info, context) - { - ErrorCode = info.GetInt32(nameof(ErrorCode)); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - Guard.Against.Null(info); - - info.AddValue(nameof(ErrorCode), ErrorCode); - - base.GetObjectData(info, context); - } } } diff --git a/src/CLI/ExitCodes.cs b/src/CLI/ExitCodes.cs index 52679a7b9..f16cf5c73 100644 --- a/src/CLI/ExitCodes.cs +++ b/src/CLI/ExitCodes.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,12 +28,15 @@ public static class ExitCodes public const int MonaiScp_ErrorList = 200; public const int MonaiScp_ErrorDelete = 201; public const int MonaiScp_ErrorCreate = 202; + public const int MonaiScp_ErrorUpdate = 203; + public const int MonaiScp_ErrorPlugIns = 204; public const int DestinationAe_ErrorList = 300; public const int DestinationAe_ErrorDelete = 301; public const int DestinationAe_ErrorCreate = 302; public const int DestinationAe_ErrorCEcho = 303; public const int DestinationAe_ErrorUpdate = 304; + public const int DestinationAe_ErrorPlugIns = 305; public const int SourceAe_ErrorList = 400; public const int SourceAe_ErrorDelete = 401; diff --git a/src/CLI/AssemblyInfo.cs b/src/CLI/InternalVIsibleTo.cs similarity index 100% rename from src/CLI/AssemblyInfo.cs rename to src/CLI/InternalVIsibleTo.cs diff --git a/src/CLI/Logging/ConsoleLogger.cs b/src/CLI/Logging/ConsoleLogger.cs index 38c4f1472..96456ecf6 100644 --- a/src/CLI/Logging/ConsoleLogger.cs +++ b/src/CLI/Logging/ConsoleLogger.cs @@ -27,20 +27,20 @@ public class ConsoleLogger : ILogger public ConsoleLogger(string name, ConsoleLoggerConfiguration configuration) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); _ = name; _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); } - public IDisposable BeginScope(TState state) + public IDisposable? BeginScope(TState state) where TState : notnull { return null; } public bool IsEnabled(LogLevel logLevel) => _configuration.MinimumLogLevel <= logLevel; - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { if (!IsEnabled(logLevel)) { diff --git a/src/CLI/Logging/ConsoleLoggerFactoryExtensions.cs b/src/CLI/Logging/ConsoleLoggerFactoryExtensions.cs index 818118b18..5536b23c5 100644 --- a/src/CLI/Logging/ConsoleLoggerFactoryExtensions.cs +++ b/src/CLI/Logging/ConsoleLoggerFactoryExtensions.cs @@ -32,7 +32,7 @@ public static class ConsoleLoggerFactoryExtensions { public static ILoggingBuilder AddInformaticsGatewayConsole(this ILoggingBuilder builder, Action configure) { - Guard.Against.Null(configure); + Guard.Against.Null(configure, nameof(configure)); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); LoggerProviderOptions.RegisterProviderOptions(builder.Services); diff --git a/src/CLI/Logging/Log.cs b/src/CLI/Logging/Log.cs index 58b2b9598..48bee6b91 100644 --- a/src/CLI/Logging/Log.cs +++ b/src/CLI/Logging/Log.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -133,8 +133,8 @@ public static partial class Log [LoggerMessage(EventId = 30043, Level = LogLevel.Debug, Message = "Available manifest names {names}.")] public static partial void AvailableManifest(this ILogger logger, string names); - [LoggerMessage(EventId = 30044, Level = LogLevel.Information, Message = "Saving appsettings.json to {path}.")] - public static partial void SaveAppSettings(this ILogger logger, string path); + [LoggerMessage(EventId = 30044, Level = LogLevel.Information, Message = "Saving {resource} to {path}.")] + public static partial void SaveAppSettings(this ILogger logger, string resource, string path); [LoggerMessage(EventId = 30045, Level = LogLevel.Information, Message = "{path} updated successfully.")] public static partial void AppSettingUpdated(this ILogger logger, string path); @@ -181,6 +181,21 @@ public static partial class Log [LoggerMessage(EventId = 30059, Level = LogLevel.Critical, Message = "Error updating DICOM source {aeTitle}: {message}")] public static partial void ErrorUpdatingDicomSource(this ILogger logger, string aeTitle, string message); + [LoggerMessage(EventId = 30060, Level = LogLevel.Information, Message = "MONAI Deploy SCP Application Entity updated:\r\n\tName: {name}\r\n\tAE Title: {aeTitle}\r\n\tGrouping: {grouping}\r\n\tTimeout: {timeout}")] + public static partial void MonaiAeTitleUpdated(this ILogger logger, string name, string aeTitle, string grouping, uint timeout); + + [LoggerMessage(EventId = 30061, Level = LogLevel.Critical, Message = "Error updating SCP Application Entity {aeTitle}: {message}")] + public static partial void ErrorUpdatingMonaiApplicationEntity(this ILogger logger, string aeTitle, string message); + + [LoggerMessage(EventId = 30062, Level = LogLevel.Information, Message = "\tPlug-ins: {plugins}")] + public static partial void MonaiAePlugIns(this ILogger logger, string plugins); + + [LoggerMessage(EventId = 30063, Level = LogLevel.Critical, Message = "Error retrieving data input plug-ins: {message}.")] + public static partial void ErrorListingDataInputPlugIns(this ILogger logger, string message); + + [LoggerMessage(EventId = 30064, Level = LogLevel.Critical, Message = "Error retrieving data output plug-ins: {message}.")] + public static partial void ErrorListingDataOutputPlugIns(this ILogger logger, string message); + // Docker Runner [LoggerMessage(EventId = 31000, Level = LogLevel.Debug, Message = "Checking for existing {applicationName} ({version}) containers...")] public static partial void CheckingExistingAppContainer(this ILogger logger, string applicationName, string version); @@ -231,6 +246,6 @@ public static partial class Log public static partial void DockerCreateWarnings(this ILogger logger, string warnings); [LoggerMessage(EventId = 31016, Level = LogLevel.Information, Message = "\tMount (plug-ins): {hostPath} => {containerPath}")] - public static partial void DockerMountPlugins(this ILogger logger, string hostPath, string containerPath); + public static partial void DockerMountPlugIns(this ILogger logger, string hostPath, string containerPath); } } diff --git a/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj b/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj index 9e39fafa6..492ff1b03 100644 --- a/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj +++ b/src/CLI/Monai.Deploy.InformaticsGateway.CLI.csproj @@ -13,12 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - Monai.Deploy.InformaticsGateway.CLI Exe - net6.0 + net8.0 true false true @@ -26,40 +25,37 @@ Apache-2.0 ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false + enable - - - + + + + + + - + - - - - All - - - - - + - + - + \ No newline at end of file diff --git a/src/CLI/Options/Common.cs b/src/CLI/Options/Common.cs index 41ab89436..7fc50b35d 100644 --- a/src/CLI/Options/Common.cs +++ b/src/CLI/Options/Common.cs @@ -25,9 +25,12 @@ public static class Common public static readonly string MigDirectory = Path.Combine(HomeDir, ".mig"); public static readonly string ContainerApplicationRootPath = "/opt/monai/ig"; public static readonly string MountedConfigFilePath = Path.Combine(ContainerApplicationRootPath, "appsettings.json"); + public static readonly string MountedNLogConfigFilePath = Path.Combine(ContainerApplicationRootPath, "nlog.config"); public static readonly string MountedDatabasePath = "/database"; public static readonly string MountedPlugInsPath = Path.Combine(ContainerApplicationRootPath, "plug-ins"); public static readonly string ConfigFilePath = Path.Combine(MigDirectory, "appsettings.json"); + public static readonly string NLogConfigFilePath = Path.Combine(MigDirectory, "nlog.config"); public static readonly string AppSettingsResourceName = $"{typeof(Program).Namespace}.Resources.appsettings.json"; + public static readonly string NLogConfigResourceName = $"{typeof(Program).Namespace}.Resources.nlog.config"; } } diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index f21f666b0..7e5e7e4cd 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -63,6 +63,8 @@ internal static Parser BuildParser() services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddHttpClient(); services.AddSingleton(p => p.GetRequiredService()); services.AddSingleton(); diff --git a/src/CLI/Properties/launchSettings.json b/src/CLI/Properties/launchSettings.json index 284f8dba9..aff7ae893 100644 --- a/src/CLI/Properties/launchSettings.json +++ b/src/CLI/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Monai.Deploy.InformaticsGateway.CLI": { "commandName": "Project", - "commandLineArgs": "aet add -a Test" + "commandLineArgs": "start" } } } \ No newline at end of file diff --git a/src/CLI/Services/ConfigurationOptionAccessor.cs b/src/CLI/Services/ConfigurationOptionAccessor.cs old mode 100644 new mode 100755 index fd134c93e..e8adade5c --- a/src/CLI/Services/ConfigurationOptionAccessor.cs +++ b/src/CLI/Services/ConfigurationOptionAccessor.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,11 @@ public interface IConfigurationOptionAccessor /// int DicomListeningPort { get; set; } + /// + /// Gets or sets the ExternalApp DICOM SCP listening port from appsettings.json. + /// + int ExternalAppDicomListeningPort { get; set; } + /// /// Gets or sets the HL7 listening port from appsettings.json. /// @@ -74,12 +79,7 @@ public interface IConfigurationOptionAccessor /// /// Gets the endpoint of the Informatics Gateway as Uri object. /// - Uri InformaticsGatewayServerUri { get; } - - /// - /// Gets the log storage path from appsettings.json. - /// - string LogStoragePath { get; } + Uri? InformaticsGatewayServerUri { get; } /// /// Gets or set the type of container runner from appsettings.json. @@ -92,6 +92,7 @@ public interface IConfigurationOptionAccessor string TempStoragePath { get; } } +#pragma warning disable CS8602 // Dereference of a possibly null reference. public class ConfigurationOptionAccessor : IConfigurationOptionAccessor { private static readonly Object SyncLock = new(); @@ -99,7 +100,7 @@ public class ConfigurationOptionAccessor : IConfigurationOptionAccessor public ConfigurationOptionAccessor(IFileSystem fileSystem) { - _fileSystem = fileSystem; + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); } public int DicomListeningPort @@ -117,6 +118,21 @@ public int DicomListeningPort } } + public int ExternalAppDicomListeningPort + { + get + { + return GetValueFromJsonPath("InformaticsGateway.dicom.scp.externalAppPort"); + } + set + { + Guard.Against.OutOfRangePort(value, nameof(ExternalAppDicomListeningPort)); + var jObject = ReadConfigurationFile(); + jObject["InformaticsGateway"]["dicom"]["scp"]["externalAppPort"] = value; + SaveConfigurationFile(jObject); + } + } + public int Hl7ListeningPort { get @@ -136,7 +152,7 @@ public string DockerImagePrefix { get { - return GetValueFromJsonPath("Cli.DockerImagePrefix"); + return GetValueFromJsonPath("Cli.DockerImagePrefix") ?? string.Empty; } } @@ -144,7 +160,7 @@ public string HostDatabaseStorageMount { get { - var path = GetValueFromJsonPath("Cli.HostDatabaseStorageMount"); + var path = GetValueFromJsonPath("Cli.HostDatabaseStorageMount") ?? string.Empty; if (path.StartsWith("~/")) { path = path.Replace("~/", $"{Common.HomeDir}/"); @@ -157,7 +173,7 @@ public string HostDataStorageMount { get { - var path = GetValueFromJsonPath("Cli.HostDataStorageMount"); + var path = GetValueFromJsonPath("Cli.HostDataStorageMount") ?? string.Empty; if (path.StartsWith("~/")) { path = path.Replace("~/", $"{Common.HomeDir}/"); @@ -170,7 +186,7 @@ public string HostPlugInsStorageMount { get { - var path = GetValueFromJsonPath("Cli.HostPlugInsStorageMount"); + var path = GetValueFromJsonPath("Cli.HostPlugInsStorageMount") ?? string.Empty; if (path.StartsWith("~/")) { path = path.Replace("~/", $"{Common.HomeDir}/"); @@ -183,7 +199,7 @@ public string HostLogsStorageMount { get { - var path = GetValueFromJsonPath("Cli.HostLogsStorageMount"); + var path = GetValueFromJsonPath("Cli.HostLogsStorageMount") ?? string.Empty; if (path.StartsWith("~/")) { path = path.Replace("~/", $"{Common.HomeDir}/"); @@ -196,7 +212,7 @@ public string InformaticsGatewayServerEndpoint { get { - return GetValueFromJsonPath("Cli.InformaticsGatewayServerEndpoint"); + return GetValueFromJsonPath("Cli.InformaticsGatewayServerEndpoint") ?? string.Empty; } set { @@ -211,28 +227,20 @@ public int InformaticsGatewayServerPort { get { - return InformaticsGatewayServerUri.Port; - } - } - - public Uri InformaticsGatewayServerUri - { - get - { - return new Uri(InformaticsGatewayServerEndpoint); + return InformaticsGatewayServerUri?.Port ?? 0; } } - public string LogStoragePath + public Uri? InformaticsGatewayServerUri { get { - var logPath = GetValueFromJsonPath("Logging.File.BasePath"); - if (logPath.StartsWith("/")) + if (InformaticsGatewayServerEndpoint is not null) { - return logPath; + return new Uri(InformaticsGatewayServerEndpoint); } - return _fileSystem.Path.Combine(Common.ContainerApplicationRootPath, logPath); + + return null; } } @@ -241,7 +249,11 @@ public Runner Runner get { var runner = GetValueFromJsonPath("Cli.Runner"); - return (Runner)Enum.Parse(typeof(Runner), runner); + if (runner is not null) + { + return (Runner)Enum.Parse(typeof(Runner), runner); + } + return Runner.Unknown; } set { @@ -255,13 +267,18 @@ public string TempStoragePath { get { - return GetValueFromJsonPath("InformaticsGateway.storage.temporary"); + return GetValueFromJsonPath("InformaticsGateway.storage.localTemporaryStoragePath") ?? string.Empty; } } - private T GetValueFromJsonPath(string jsonPath) + private T? GetValueFromJsonPath(string jsonPath) { - return ReadConfigurationFile().SelectToken(jsonPath).Value(); + var token = ReadConfigurationFile().SelectToken(jsonPath); + + if (token is not null) + return token.Value(); + + return default; } private JObject ReadConfigurationFile() @@ -284,3 +301,4 @@ private void SaveConfigurationFile(JObject jObject) } } } +#pragma warning restore CS8602 // Dereference of a possibly null reference. diff --git a/src/CLI/Services/ConfigurationService.cs b/src/CLI/Services/ConfigurationService.cs index c46c16960..b0c183808 100644 --- a/src/CLI/Services/ConfigurationService.cs +++ b/src/CLI/Services/ConfigurationService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; namespace Monai.Deploy.InformaticsGateway.CLI.Services @@ -35,13 +36,20 @@ public class ConfigurationService : IConfigurationService public bool IsConfigExists => _fileSystem.File.Exists(Common.ConfigFilePath); public IConfigurationOptionAccessor Configurations { get; } + public INLogConfigurationOptionAccessor NLogConfigurations { get; } - public ConfigurationService(ILogger logger, IFileSystem fileSystem, IEmbeddedResource embeddedResource) + public ConfigurationService( + ILogger logger, + IFileSystem fileSystem, + IEmbeddedResource embeddedResource, + IConfigurationOptionAccessor configurationOptionAccessor, + INLogConfigurationOptionAccessor nLogConfigurationOptionAccessor) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _embeddedResource = embeddedResource ?? throw new ArgumentNullException(nameof(embeddedResource)); - Configurations = new ConfigurationOptionAccessor(fileSystem); + Configurations = configurationOptionAccessor ?? throw new ArgumentNullException(nameof(configurationOptionAccessor)); + NLogConfigurations = nLogConfigurationOptionAccessor ?? throw new ArgumentNullException(nameof(nLogConfigurationOptionAccessor)); } public void CreateConfigDirectoryIfNotExist() @@ -55,22 +63,31 @@ public void CreateConfigDirectoryIfNotExist() public async Task Initialize(CancellationToken cancellationToken) { _logger.DebugMessage("Reading default application configurations..."); - using var stream = _embeddedResource.GetManifestResourceStream(Common.AppSettingsResourceName); + await WriteConfigFile(Common.AppSettingsResourceName, Common.ConfigFilePath, cancellationToken).ConfigureAwait(false); + await WriteConfigFile(Common.NLogConfigResourceName, Common.NLogConfigFilePath, cancellationToken).ConfigureAwait(false); + } + + public async Task WriteConfigFile(string resourceName, string outputPath, CancellationToken cancellationToken) + { + Guard.Against.NullOrWhiteSpace(resourceName, nameof(resourceName)); + Guard.Against.NullOrWhiteSpace(outputPath, nameof(outputPath)); + + using var stream = _embeddedResource.GetManifestResourceStream(resourceName); if (stream is null) { _logger.AvailableManifest(string.Join(",", Assembly.GetExecutingAssembly().GetManifestResourceNames())); - throw new ConfigurationException($"Default configuration file could not be loaded, please reinstall the CLI."); + throw new ConfigurationException($"Default configuration file: {resourceName} could not be loaded, please reinstall the CLI."); } CreateConfigDirectoryIfNotExist(); - _logger.SaveAppSettings(Common.ConfigFilePath); - using (var fileStream = _fileSystem.FileStream.Create(Common.ConfigFilePath, FileMode.Create)) + _logger.SaveAppSettings(resourceName, outputPath); + using (var fileStream = _fileSystem.FileStream.New(outputPath, FileMode.Create)) { await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false); } - _logger.AppSettingUpdated(Common.ConfigFilePath); + _logger.AppSettingUpdated(outputPath); } } } diff --git a/src/CLI/Services/ConfirmationPrompt.cs b/src/CLI/Services/ConfirmationPrompt.cs index 156f2c7ce..bcef296a7 100644 --- a/src/CLI/Services/ConfirmationPrompt.cs +++ b/src/CLI/Services/ConfirmationPrompt.cs @@ -28,7 +28,7 @@ internal class ConfirmationPrompt : IConfirmationPrompt { public bool ShowConfirmationPrompt(string message) { - Guard.Against.NullOrWhiteSpace(message); + Guard.Against.NullOrWhiteSpace(message, nameof(message)); Console.Write($"{message} [y/N]: "); var key = Console.ReadKey(); diff --git a/src/CLI/Services/ControlService.cs b/src/CLI/Services/ControlService.cs index 3ccc2ae22..502e34cfc 100644 --- a/src/CLI/Services/ControlService.cs +++ b/src/CLI/Services/ControlService.cs @@ -80,7 +80,7 @@ public async Task StopService(CancellationToken cancellationToken = default) if (!applicationVersions.IsNullOrEmpty()) { - foreach (var applicationVersion in applicationVersions) + foreach (var applicationVersion in applicationVersions!) { var runnerState = await runner.IsApplicationRunning(applicationVersion, cancellationToken).ConfigureAwait(false); diff --git a/src/CLI/Services/DockerRunner.cs b/src/CLI/Services/DockerRunner.cs old mode 100644 new mode 100755 index 41bf03d2a..f98383e6b --- a/src/CLI/Services/DockerRunner.cs +++ b/src/CLI/Services/DockerRunner.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ public DockerRunner(ILogger logger, IConfigurationService configur public async Task IsApplicationRunning(ImageVersion imageVersion, CancellationToken cancellationToken = default) { - Guard.Against.Null(imageVersion); + Guard.Against.Null(imageVersion, nameof(imageVersion)); _logger.CheckingExistingAppContainer(Strings.ApplicationName, imageVersion.Version); var parameters = new ContainersListParameters @@ -64,26 +64,26 @@ public async Task IsApplicationRunning(ImageVersion imageVersion, C return new RunnerState { IsRunning = false }; } - return new RunnerState { IsRunning = true, Id = matches.First().ID }; + return new RunnerState { IsRunning = true, Id = matches[0].ID }; } - public async Task GetLatestApplicationVersion(CancellationToken cancellationToken = default) + public async Task GetLatestApplicationVersion(CancellationToken cancellationToken = default) => await GetLatestApplicationVersion(_configurationService.Configurations.DockerImagePrefix, cancellationToken).ConfigureAwait(false); - public async Task GetLatestApplicationVersion(string version, CancellationToken cancellationToken = default) + public async Task GetLatestApplicationVersion(string version, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(version); + Guard.Against.NullOrWhiteSpace(version, nameof(version)); var results = await GetApplicationVersions(version, cancellationToken).ConfigureAwait(false); return results?.OrderByDescending(p => p.Created).FirstOrDefault(); } - public async Task> GetApplicationVersions(CancellationToken cancellationToken = default) + public async Task?> GetApplicationVersions(CancellationToken cancellationToken = default) => await GetApplicationVersions(_configurationService.Configurations.DockerImagePrefix, cancellationToken).ConfigureAwait(false); - public async Task> GetApplicationVersions(string version, CancellationToken cancellationToken = default) + public async Task?> GetApplicationVersions(string version, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(version); + Guard.Against.NullOrWhiteSpace(version, nameof(version)); _logger.ConnectingToDocker(); var parameters = new ImagesListParameters @@ -98,12 +98,16 @@ public async Task> GetApplicationVersions(string version, Ca }; _logger.RetrievingImagesFromDocker(); var images = await _dockerClient.Images.ListImagesAsync(parameters, cancellationToken).ConfigureAwait(false); - return images?.Select(p => new ImageVersion { Version = p.RepoTags.First(), Id = p.ID, Created = p.Created }).ToList(); + if (images is null) + { + return null; + } + return images.Select(p => new ImageVersion { Version = p.RepoTags[0], Id = p.ID, Created = p.Created }).ToList(); } public async Task StartApplication(ImageVersion imageVersion, CancellationToken cancellationToken = default) { - Guard.Against.Null(imageVersion); + Guard.Against.Null(imageVersion, nameof(imageVersion)); _logger.CreatingDockerContainer(Strings.ApplicationName, imageVersion.Version, imageVersion.IdShort); var createContainerParams = new CreateContainerParameters @@ -114,6 +118,7 @@ public async Task StartApplication(ImageVersion imageVersion, Cancellation }; createContainerParams.HostConfig.PortBindings = new Dictionary>(); + createContainerParams.HostConfig.NetworkMode = "monaideploy"; _logger.DockerPrtBinding(_configurationService.Configurations.DicomListeningPort); createContainerParams.ExposedPorts.Add($"{_configurationService.Configurations.DicomListeningPort}/tcp", new EmptyStruct()); @@ -135,11 +140,11 @@ public async Task StartApplication(ImageVersion imageVersion, Cancellation _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostDatabaseStorageMount); createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostDatabaseStorageMount, Target = Common.MountedDatabasePath }); - _logger.DockerMountAppLogs(_configurationService.Configurations.HostLogsStorageMount, _configurationService.Configurations.LogStoragePath); + _logger.DockerMountAppLogs(_configurationService.Configurations.HostLogsStorageMount, _configurationService.NLogConfigurations.LogStoragePath); _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostLogsStorageMount); - createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostLogsStorageMount, Target = _configurationService.Configurations.LogStoragePath }); + createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostLogsStorageMount, Target = _configurationService.NLogConfigurations.LogStoragePath }); - _logger.DockerMountPlugins(_configurationService.Configurations.HostPlugInsStorageMount, Common.MountedPlugInsPath); + _logger.DockerMountPlugIns(_configurationService.Configurations.HostPlugInsStorageMount, Common.MountedPlugInsPath); _fileSystem.Directory.CreateDirectoryIfNotExists(_configurationService.Configurations.HostPlugInsStorageMount); createContainerParams.HostConfig.Mounts.Add(new Mount { Type = "bind", ReadOnly = false, Source = _configurationService.Configurations.HostPlugInsStorageMount, Target = Common.MountedPlugInsPath }); @@ -168,7 +173,7 @@ public async Task StartApplication(ImageVersion imageVersion, Cancellation public async Task StopApplication(RunnerState runnerState, CancellationToken cancellationToken = default) { - Guard.Against.Null(runnerState); + Guard.Against.Null(runnerState, nameof(runnerState)); _logger.DockerContainerStopping(Strings.ApplicationName, runnerState.IdShort); var result = await _dockerClient.Containers.StopContainerAsync(runnerState.Id, new ContainerStopParameters() { WaitBeforeKillSeconds = 60 }, cancellationToken).ConfigureAwait(false); diff --git a/src/CLI/Services/EmbeddedResource.cs b/src/CLI/Services/EmbeddedResource.cs index b723e2bee..bc449d6ee 100644 --- a/src/CLI/Services/EmbeddedResource.cs +++ b/src/CLI/Services/EmbeddedResource.cs @@ -21,15 +21,16 @@ namespace Monai.Deploy.InformaticsGateway.CLI.Services { public interface IEmbeddedResource { - Stream GetManifestResourceStream(string name); + Stream? GetManifestResourceStream(string name); } public class EmbeddedResource : IEmbeddedResource { - public Stream GetManifestResourceStream(string name) + public Stream? GetManifestResourceStream(string name) { - Guard.Against.NullOrWhiteSpace(name); - return GetType().Assembly.GetManifestResourceStream(Common.AppSettingsResourceName); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + return GetType().Assembly.GetManifestResourceStream(name); } } } diff --git a/src/CLI/Services/IConfigurationService.cs b/src/CLI/Services/IConfigurationService.cs index a4a0d8fa0..77cfe108c 100644 --- a/src/CLI/Services/IConfigurationService.cs +++ b/src/CLI/Services/IConfigurationService.cs @@ -26,6 +26,11 @@ public interface IConfigurationService /// IConfigurationOptionAccessor Configurations { get; } + /// + /// Gets the configurations inside nlog.config + /// + INLogConfigurationOptionAccessor NLogConfigurations { get; } + /// /// Gets whether the configuration file exists or not. /// diff --git a/src/CLI/Services/IContainerRunner.cs b/src/CLI/Services/IContainerRunner.cs index 358fb8d5e..c533ea713 100644 --- a/src/CLI/Services/IContainerRunner.cs +++ b/src/CLI/Services/IContainerRunner.cs @@ -34,7 +34,7 @@ public class RunnerState /// /// ID of the running application provided by the orchestration engine. /// - public string Id { get; set; } + public string Id { get; set; } = string.Empty; /// /// Shorter version of the ID, with 12 characters. @@ -56,13 +56,13 @@ public class ImageVersion /// /// Version or label of the application/image detected. /// - public string Version { get; set; } + public string Version { get; set; } = string.Empty; /// /// Unique ID provided by the orchestration engine. /// /// - public string Id { get; set; } + public string Id { get; set; } = string.Empty; /// /// Shorter version of the ID, with 12 characters. @@ -86,13 +86,13 @@ public interface IContainerRunner { Task IsApplicationRunning(ImageVersion imageVersion, CancellationToken cancellationToken = default); - Task GetLatestApplicationVersion(CancellationToken cancellationToken = default); + Task GetLatestApplicationVersion(CancellationToken cancellationToken = default); - Task GetLatestApplicationVersion(string version, CancellationToken cancellationToken = default); + Task GetLatestApplicationVersion(string version, CancellationToken cancellationToken = default); - Task> GetApplicationVersions(CancellationToken cancellationToken = default); + Task?> GetApplicationVersions(CancellationToken cancellationToken = default); - Task> GetApplicationVersions(string version, CancellationToken cancellationToken = default); + Task?> GetApplicationVersions(string version, CancellationToken cancellationToken = default); Task StartApplication(ImageVersion imageVersion, CancellationToken cancellationToken = default); diff --git a/src/CLI/Services/NLogConfigurationOptionAccessor.cs b/src/CLI/Services/NLogConfigurationOptionAccessor.cs new file mode 100644 index 000000000..7ea3ad449 --- /dev/null +++ b/src/CLI/Services/NLogConfigurationOptionAccessor.cs @@ -0,0 +1,61 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO.Abstractions; +using System.Xml; + +namespace Monai.Deploy.InformaticsGateway.CLI.Services +{ + public interface INLogConfigurationOptionAccessor + { + /// + /// Gets the logs directory path. + /// + string LogStoragePath { get; } + } + + public class NLogConfigurationOptionAccessor : INLogConfigurationOptionAccessor + { + public const string NlogSchema = "http://www.nlog-project.org/schemas/NLog.xsd"; + private readonly XmlDocument _xmlDocument; + private readonly XmlNamespaceManager _namespaceManager; + + public NLogConfigurationOptionAccessor(IFileSystem fileSystem) + { + if (fileSystem is null) + { + throw new ArgumentNullException(nameof(fileSystem)); + } + + var xml = fileSystem.File.ReadAllText(Common.NLogConfigFilePath); + _xmlDocument = new XmlDocument(); + _xmlDocument.LoadXml(xml); + _namespaceManager = new XmlNamespaceManager(_xmlDocument.NameTable); + _namespaceManager.AddNamespace("ns", NlogSchema); + } + + public string LogStoragePath + { + get + { + var value = _xmlDocument.SelectSingleNode("//ns:variable[@name='logDir']/@value", _namespaceManager)?.InnerText; + value = value?.Replace("${basedir}", Common.ContainerApplicationRootPath); + return value ?? string.Empty; + } + } + } +} diff --git a/src/CLI/Services/Runner.cs b/src/CLI/Services/Runner.cs index 38fe07c95..704ed5bd5 100644 --- a/src/CLI/Services/Runner.cs +++ b/src/CLI/Services/Runner.cs @@ -18,6 +18,7 @@ namespace Monai.Deploy.InformaticsGateway.CLI.Services { public enum Runner { + Unknown, Docker, Kubernetes, Helm, diff --git a/src/CLI/Test/AetCommandTest.cs b/src/CLI/Test/AetCommandTest.cs old mode 100644 new mode 100755 index b90c73b9e..9852ccf0a --- a/src/CLI/Test/AetCommandTest.cs +++ b/src/CLI/Test/AetCommandTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.CLI.Services; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.SharedTest; @@ -100,7 +100,42 @@ public async Task AetAdd_Command() { Name = result.CommandResult.Children[0].Tokens[0].Value, AeTitle = result.CommandResult.Children[1].Tokens[0].Value, - Workflows = result.CommandResult.Children[2].Tokens.Select(p => p.Value).ToList() + Workflows = result.CommandResult.Children[2].Tokens.Select(p => p.Value).ToList(), + }; + Assert.Equal("MyName", entity.Name); + Assert.Equal("MyAET", entity.AeTitle); + Assert.Collection(entity.Workflows, + item => item.Equals("App"), + item => item.Equals("MyCoolApp"), + item => item.Equals("TheApp")); + + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(entity); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify( + p => p.MonaiScpAeTitle.Create( + It.Is(o => o.AeTitle == entity.AeTitle && o.Name == entity.Name && Enumerable.SequenceEqual(o.Workflows, entity.Workflows)), + It.IsAny()), Times.Once()); + } + + [Fact(DisplayName = "aet add comand with plug-ins")] + public async Task AetAdd_Command_WithPlugIns() + { + var command = "aet add -n MyName -a MyAET --workflows App MyCoolApp TheApp --plugins \"PlugInTypeA\" \"PlugInTypeB\""; + var result = _paser.Parse(command); + Assert.Equal(ExitCodes.Success, result.Errors.Count); + + var entity = new MonaiApplicationEntity() + { + Name = result.CommandResult.Children[0].Tokens[0].Value, + AeTitle = result.CommandResult.Children[1].Tokens[0].Value, + Workflows = result.CommandResult.Children[2].Tokens.Select(p => p.Value).ToList(), + PlugInAssemblies = result.CommandResult.Children[3].Tokens.Select(p => p.Value).ToList(), }; Assert.Equal("MyName", entity.Name); Assert.Equal("MyAET", entity.AeTitle); @@ -108,6 +143,9 @@ public async Task AetAdd_Command() item => item.Equals("App"), item => item.Equals("MyCoolApp"), item => item.Equals("TheApp")); + Assert.Collection(entity.PlugInAssemblies, + item => item.Equals("PlugInTypeA"), + item => item.Equals("PlugInTypeB")); _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.Create(It.IsAny(), It.IsAny())) .ReturnsAsync(entity); @@ -331,5 +369,162 @@ public async Task AetList_Command_Empty() _logger.VerifyLogging("No MONAI SCP Application Entities configured.", LogLevel.Warning, Times.Once()); } + + [Fact(DisplayName = "aet update command")] + public async Task AetUpdate_Command() + { + var command = "aet update -n MyName --workflows App MyCoolApp TheApp -i A B C -s D E F -p PlugInAssemblyA PlugInAssemblyB"; + var result = _paser.Parse(command); + Assert.Equal(ExitCodes.Success, result.Errors.Count); + + var entity = new MonaiApplicationEntity() + { + Name = result.CommandResult.Children[0].Tokens[0].Value, + AeTitle = "MyAET", + Workflows = result.CommandResult.Children[1].Tokens.Select(p => p.Value).ToList(), + IgnoredSopClasses = result.CommandResult.Children[2].Tokens.Select(p => p.Value).ToList(), + AllowedSopClasses = result.CommandResult.Children[3].Tokens.Select(p => p.Value).ToList(), + PlugInAssemblies = result.CommandResult.Children[4].Tokens.Select(p => p.Value).ToList(), + }; + + Assert.Equal("MyName", entity.Name); + Assert.Equal("MyAET", entity.AeTitle); + Assert.Collection(entity.Workflows, + item => item.Equals("App"), + item => item.Equals("MyCoolApp"), + item => item.Equals("TheApp")); + Assert.Collection(entity.AllowedSopClasses, + item => item.Equals("D"), + item => item.Equals("E"), + item => item.Equals("F")); + Assert.Collection(entity.IgnoredSopClasses, + item => item.Equals("A"), + item => item.Equals("B"), + item => item.Equals("C")); + Assert.Collection(entity.PlugInAssemblies, + item => item.Equals("PlugInAssemblyA"), + item => item.Equals("PlugInAssemblyB")); + + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.Update(It.IsAny(), It.IsAny())) + .ReturnsAsync(entity); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify( + p => p.MonaiScpAeTitle.Update( + It.Is(o => o.Name == entity.Name && + o.Timeout == entity.Timeout && + o.Grouping == entity.Grouping && + Enumerable.SequenceEqual(o.Workflows, entity.Workflows) && + Enumerable.SequenceEqual(o.AllowedSopClasses, entity.AllowedSopClasses) && + Enumerable.SequenceEqual(o.IgnoredSopClasses, entity.IgnoredSopClasses)), + It.IsAny()), Times.Once()); + } + + [Fact(DisplayName = "aet update command exception")] + public async Task AetUpdate_Command_Exception() + { + var command = "aet update -n MyName --workflows App MyCoolApp TheApp -i A B C -s D E F"; + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.Update(It.IsAny(), It.IsAny())) + .Throws(new Exception("error")); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.MonaiScp_ErrorUpdate, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.MonaiScpAeTitle.Update(It.IsAny(), It.IsAny()), Times.Once()); + + _logger.VerifyLoggingMessageBeginsWith("Error updating SCP Application Entity", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "aet update command configuration exception")] + public async Task AetUpdate_Command_ConfigurationException() + { + var command = "aet update -n MyName --workflows App MyCoolApp TheApp -i A B C -s D E F"; + _configurationService.SetupGet(p => p.IsInitialized).Returns(false); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Config_NotConfigured, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Never()); + _informaticsGatewayClient.Verify(p => p.DicomDestinations.List(It.IsAny()), Times.Never()); + + _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "aet plugins comand")] + public async Task AetPlugIns_Command() + { + var command = "aet plugins"; + var result = _paser.Parse(command); + Assert.Equal(ExitCodes.Success, result.Errors.Count); + + var entries = new Dictionary { { "A", "1" }, { "B", "2" } }; + + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.PlugIns(It.IsAny())) + .ReturnsAsync(entries); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.MonaiScpAeTitle.PlugIns(It.IsAny()), Times.Once()); + } + + [Fact(DisplayName = "aet plugins comand exception")] + public async Task AetPlugIns_Command_Exception() + { + var command = "aet plugins"; + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.PlugIns(It.IsAny())) + .Throws(new Exception("error")); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.MonaiScp_ErrorPlugIns, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.MonaiScpAeTitle.PlugIns(It.IsAny()), Times.Once()); + + _logger.VerifyLoggingMessageBeginsWith("Error retrieving data input plug-ins", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "aet plugins comand configuration exception")] + public async Task AetPlugIns_Command_ConfigurationException() + { + var command = "aet plugins"; + _configurationService.SetupGet(p => p.IsInitialized).Returns(false); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Config_NotConfigured, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Never()); + _informaticsGatewayClient.Verify(p => p.MonaiScpAeTitle.PlugIns(It.IsAny()), Times.Never()); + + _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "aet plugins comand empty")] + public async Task AetPlugIns_Command_Empty() + { + var command = "aet plugins"; + _informaticsGatewayClient.Setup(p => p.MonaiScpAeTitle.PlugIns(It.IsAny())) + .ReturnsAsync(new Dictionary()); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.MonaiScpAeTitle.PlugIns(It.IsAny()), Times.Once()); + + _logger.VerifyLogging("No MONAI SCP Application Entities configured.", LogLevel.Warning, Times.Once()); + } } } diff --git a/src/CLI/Test/ConfigurationOptionAccessorTest.cs b/src/CLI/Test/ConfigurationOptionAccessorTest.cs new file mode 100644 index 000000000..1a3a6b974 --- /dev/null +++ b/src/CLI/Test/ConfigurationOptionAccessorTest.cs @@ -0,0 +1,192 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using Ardalis.GuardClauses; +using Monai.Deploy.InformaticsGateway.CLI.Services; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.CLI.Test +{ + public class ConfigurationOptionAccessorTest + { + public ConfigurationOptionAccessorTest() + { + } + + [Fact(DisplayName = "ConfigurationOptionAccessor Constructor")] + public void DockerRunner_Constructor() + { + Assert.Throws(() => new ConfigurationOptionAccessor(null)); + } + + [Fact] + public void DicomListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"dicom\": {\"scp\": {\"port\": 104}}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(104, configurationOptionAccessor.DicomListeningPort); + } + + [Fact] + public void DicomListeningPort_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"dicom\": {\"scp\": {\"port\": 104}}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.DicomListeningPort = 1000; + Assert.Equal(1000, configurationOptionAccessor.DicomListeningPort); + } + + [Fact] + public void Hl7ListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"hl7\": {\"port\": 2575}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(2575, configurationOptionAccessor.Hl7ListeningPort); + } + + [Fact] + public void Hl7ListeningPort_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"hl7\": {\"port\": 2575}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.Hl7ListeningPort = 1000; + Assert.Equal(1000, configurationOptionAccessor.Hl7ListeningPort); + } + + [Fact] + public void DockerImagePrefix_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"DockerImagePrefix\": \"ghcr.io/project-monai/monai-deploy-informatics-gateway\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal("ghcr.io/project-monai/monai-deploy-informatics-gateway", configurationOptionAccessor.DockerImagePrefix); + } + + [Fact] + public void HostDatabaseStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostDatabaseStorageMount\": \"~/.mig/database\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/database", configurationOptionAccessor.HostDatabaseStorageMount); + } + + [Fact] + public void HostDataStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostDataStorageMount\": \"~/.mig/data\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/data", configurationOptionAccessor.HostDataStorageMount); + } + + [Fact] + public void HostPlugInsStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostPlugInsStorageMount\": \"~/.mig/plug-ins\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/plug-ins", configurationOptionAccessor.HostPlugInsStorageMount); + } + + [Fact] + public void HostLogsStorageMount_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"HostLogsStorageMount\": \"~/.mig/logs\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.HomeDir}/.mig/logs", configurationOptionAccessor.HostLogsStorageMount); + } + + [Fact] + public void InformaticsGatewayServerEndpoint_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal("http://localhost:5000", configurationOptionAccessor.InformaticsGatewayServerEndpoint); + } + + [Fact] + public void InformaticsGatewayServerEndpoint_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.InformaticsGatewayServerEndpoint = "http://hello-world"; + Assert.Equal("http://hello-world", configurationOptionAccessor.InformaticsGatewayServerEndpoint); + } + + [Fact] + public void InformaticsGatewayServerPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(5000, configurationOptionAccessor.InformaticsGatewayServerPort); + } + + [Fact] + public void InformaticsGatewayServerUri_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"InformaticsGatewayServerEndpoint\": \"http://localhost:5000\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(new Uri("http://localhost:5000"), configurationOptionAccessor.InformaticsGatewayServerUri); + } + + [Fact] + public void Runner_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"Runner\": \"Docker\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + + Assert.Equal(Runner.Docker, configurationOptionAccessor.Runner); + } + + [Fact] + public void Runner_Set_UpdatesValue() + { + var fileSystem = SetupFileSystem("{\"Cli\": {\"Runner\": \"Docker\"}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + configurationOptionAccessor.Runner = Runner.Kubernetes; + Assert.Equal(Runner.Kubernetes, configurationOptionAccessor.Runner); + } + + [Fact] + public void TempStoragePath_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem("{\"InformaticsGateway\": {\"storage\": {\"localTemporaryStoragePath\": \"/payloads\"}}}"); + var configurationOptionAccessor = new ConfigurationOptionAccessor(fileSystem); + Assert.Equal("/payloads", configurationOptionAccessor.TempStoragePath); + } + + private IFileSystem SetupFileSystem(string config) + { + Guard.Against.NullOrWhiteSpace(config, nameof(config)); + + return new MockFileSystem(new Dictionary + { + {Common.ConfigFilePath, new MockFileData(config) } + }); + } + } +} diff --git a/src/CLI/Test/ConfigurationServiceTest.cs b/src/CLI/Test/ConfigurationServiceTest.cs index 3d68956e9..bd797f106 100644 --- a/src/CLI/Test/ConfigurationServiceTest.cs +++ b/src/CLI/Test/ConfigurationServiceTest.cs @@ -17,6 +17,7 @@ using System; using System.IO; using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -32,29 +33,35 @@ public class ConfigurationServiceTest private readonly Mock> _logger; private readonly Mock _fileSystem; private readonly Mock _embeddedResource; + private readonly Mock _configurationOptionAccessor; + private readonly Mock _nLogConfigurationOptionAccessor; public ConfigurationServiceTest() { _logger = new Mock>(); _fileSystem = new Mock(); _embeddedResource = new Mock(); + _configurationOptionAccessor = new Mock(); + _nLogConfigurationOptionAccessor = new Mock(); } [Fact(DisplayName = "ConfigurationServiceTest constructor")] public void ConfigurationServiceTest_Constructor() { - Assert.Throws(() => new ConfigurationService(null, null, null)); - Assert.Throws(() => new ConfigurationService(_logger.Object, null, null)); - Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, null)); + Assert.Throws(() => new ConfigurationService(null, null, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, null, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, null, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, null, null)); + Assert.Throws(() => new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, null)); - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); Assert.NotNull(svc.Configurations); } [Fact(DisplayName = "CreateConfigDirectoryIfNotExist creates directory")] public void CreateConfigDirectoryIfNotExist_CreateDirectory() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(false); svc.CreateConfigDirectoryIfNotExist(); @@ -65,7 +72,7 @@ public void CreateConfigDirectoryIfNotExist_CreateDirectory() [Fact(DisplayName = "CreateConfigDirectoryIfNotExist skips creating directory")] public void CreateConfigDirectoryIfNotExist_SkipsCreation() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); svc.CreateConfigDirectoryIfNotExist(); @@ -76,7 +83,7 @@ public void CreateConfigDirectoryIfNotExist_SkipsCreation() [Fact(DisplayName = "IsInitialized")] public void IsInitialized() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); _fileSystem.Setup(p => p.File.Exists(It.IsAny())).Returns(true); @@ -88,7 +95,7 @@ public void IsInitialized() [Fact(DisplayName = "IsConfigExists")] public void ConfigurationExists() { - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); _fileSystem.Setup(p => p.File.Exists(It.IsAny())).Returns(true); Assert.True(svc.IsConfigExists); @@ -101,40 +108,35 @@ public async Task Initialize_ShallThrowWhenConfigReousrceIsMissing() { _embeddedResource.Setup(p => p.GetManifestResourceStream(It.IsAny())).Returns(default(Stream)); - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); await Assert.ThrowsAsync(async () => await svc.Initialize(CancellationToken.None)); } - [Fact(DisplayName = "Initialize creates the config file")] - public async Task Initialize_CreatesTheConfigFile() + [Fact(DisplayName = "Initialize creates the config files")] + public async Task Initialize_CreatesTheConfigFiles() { + var fileSystem = new MockFileSystem(); var testString = "hello world"; - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); - var mockStream = new Mock(); - byte[] bytesWritten = null; - - mockStream.Setup(p => p.FlushAsync(It.IsAny())); - mockStream.Setup(p => p.Close()); - mockStream.Setup(p => p.CanWrite).Returns(true); - mockStream.Setup(s => s.WriteAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Callback((byte[] bytes, int offset, int count, CancellationToken cancellationToken) => - { - bytesWritten = new byte[bytes.Length]; - bytes.CopyTo(bytesWritten, 0); - }); + var appConfigMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + var nLogConfigMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(testString)); + var mockSteam = new Mock(); - _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); - _fileSystem.Setup(p => p.FileStream.Create(It.IsAny(), It.IsAny())).Returns(mockStream.Object); - _embeddedResource.Setup(p => p.GetManifestResourceStream(It.IsAny())).Returns(memoryStream); + _embeddedResource.Setup(p => p.GetManifestResourceStream(It.Is(p => p == Common.AppSettingsResourceName))).Returns(appConfigMemoryStream); + _embeddedResource.Setup(p => p.GetManifestResourceStream(It.Is(p => p == Common.NLogConfigResourceName))).Returns(nLogConfigMemoryStream); - var svc = new ConfigurationService(_logger.Object, _fileSystem.Object, _embeddedResource.Object); + var svc = new ConfigurationService(_logger.Object, fileSystem, _embeddedResource.Object, _configurationOptionAccessor.Object, _nLogConfigurationOptionAccessor.Object); await svc.Initialize(CancellationToken.None); _embeddedResource.Verify(p => p.GetManifestResourceStream(Common.AppSettingsResourceName), Times.Once()); - _fileSystem.Verify(p => p.FileStream.Create(Common.ConfigFilePath, FileMode.Create), Times.Once()); - mockStream.Verify(p => p.FlushAsync(It.IsAny()), Times.Once()); - mockStream.Verify(p => p.Close(), Times.Once()); + _embeddedResource.Verify(p => p.GetManifestResourceStream(Common.NLogConfigResourceName), Times.Once()); + + var bytesWritten = await fileSystem.File.ReadAllBytesAsync(Common.ConfigFilePath).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(testString, Encoding.UTF8.GetString(bytesWritten)); + + bytesWritten = await fileSystem.File.ReadAllBytesAsync(Common.NLogConfigFilePath).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(testString, Encoding.UTF8.GetString(bytesWritten)); + bytesWritten = await fileSystem.File.ReadAllBytesAsync(Common.NLogConfigFilePath).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(testString, Encoding.UTF8.GetString(bytesWritten)); } } diff --git a/src/CLI/Test/ControlExceptionTest.cs b/src/CLI/Test/ControlExceptionTest.cs deleted file mode 100644 index 80e61d001..000000000 --- a/src/CLI/Test/ControlExceptionTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021-2022 MONAI Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using Xunit; - -namespace Monai.Deploy.InformaticsGateway.CLI.Test -{ - public class ControlExceptionTest - { - [Fact] - public void TestControlExceptionSerialization() - { - var exception = new ControlException(100, "error"); - - var data = SerializeToBytes(exception); - var result = DeserializeFromBytes(data); - - Assert.Equal(exception.ErrorCode, result.ErrorCode); - Assert.Equal(exception.Message, result.Message); - } - - private static byte[] SerializeToBytes(T e) - { - using var stream = new MemoryStream(); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - new BinaryFormatter().Serialize(stream, e); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - return stream.GetBuffer(); - } - - private static T DeserializeFromBytes(byte[] bytes) - { - using var stream = new MemoryStream(bytes); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - return (T)new BinaryFormatter().Deserialize(stream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - } - } -} diff --git a/src/CLI/Test/DestinationCommandTest.cs b/src/CLI/Test/DestinationCommandTest.cs old mode 100644 new mode 100755 index 7b742171a..0d9dd4cd0 --- a/src/CLI/Test/DestinationCommandTest.cs +++ b/src/CLI/Test/DestinationCommandTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.CLI.Services; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.SharedTest; @@ -339,7 +339,6 @@ public async Task DstCEcho_Command_ConfigurationException() _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); } - [Fact(DisplayName = "dst update command")] public async Task DstUpdate_Command() { @@ -406,5 +405,75 @@ public async Task DstUpdate_Command_ConfigurationException() _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); } + + [Fact(DisplayName = "dst plugins comand")] + public async Task DstPlugIns_Command() + { + var command = "dst plugins"; + var result = _paser.Parse(command); + Assert.Equal(ExitCodes.Success, result.Errors.Count); + + var entries = new Dictionary { { "A", "1" }, { "B", "2" } }; + + _informaticsGatewayClient.Setup(p => p.DicomDestinations.PlugIns(It.IsAny())) + .ReturnsAsync(entries); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.DicomDestinations.PlugIns(It.IsAny()), Times.Once()); + } + + [Fact(DisplayName = "dst plugins comand exception")] + public async Task DstPlugIns_Command_Exception() + { + var command = "dst plugins"; + _informaticsGatewayClient.Setup(p => p.DicomDestinations.PlugIns(It.IsAny())) + .Throws(new Exception("error")); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.DestinationAe_ErrorPlugIns, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.DicomDestinations.PlugIns(It.IsAny()), Times.Once()); + + _logger.VerifyLoggingMessageBeginsWith("Error retrieving data output plug-ins", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "dst plugins comand configuration exception")] + public async Task DstPlugIns_Command_ConfigurationException() + { + var command = "dst plugins"; + _configurationService.SetupGet(p => p.IsInitialized).Returns(false); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Config_NotConfigured, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Never()); + _informaticsGatewayClient.Verify(p => p.DicomDestinations.PlugIns(It.IsAny()), Times.Never()); + + _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); + } + + [Fact(DisplayName = "dst plugins comand empty")] + public async Task DstPlugIns_Command_Empty() + { + var command = "dst plugins"; + _informaticsGatewayClient.Setup(p => p.DicomDestinations.PlugIns(It.IsAny())) + .ReturnsAsync(new Dictionary()); + + int exitCode = await _paser.InvokeAsync(command); + + Assert.Equal(ExitCodes.Success, exitCode); + + _informaticsGatewayClient.Verify(p => p.ConfigureServiceUris(It.IsAny()), Times.Once()); + _informaticsGatewayClient.Verify(p => p.DicomDestinations.PlugIns(It.IsAny()), Times.Once()); + + _logger.VerifyLogging("No MONAI SCP Application Entities configured.", LogLevel.Warning, Times.Once()); + } } } diff --git a/src/CLI/Test/DockerRunnerTest.cs b/src/CLI/Test/DockerRunnerTest.cs index 2ecb446d5..b992bb6d2 100644 --- a/src/CLI/Test/DockerRunnerTest.cs +++ b/src/CLI/Test/DockerRunnerTest.cs @@ -163,7 +163,7 @@ public async Task StartApplication() _configurationService.SetupGet(p => p.Configurations.HostLogsStorageMount).Returns("/logs"); _configurationService.SetupGet(p => p.Configurations.HostPlugInsStorageMount).Returns("/plug-ins"); _configurationService.SetupGet(p => p.Configurations.TempStoragePath).Returns("/tempdata"); - _configurationService.SetupGet(p => p.Configurations.LogStoragePath).Returns("/templogs"); + _configurationService.SetupGet(p => p.NLogConfigurations.LogStoragePath).Returns("/templogs"); Assert.True(await runner.StartApplication(image, CancellationToken.None)); Assert.False(await runner.StartApplication(image, CancellationToken.None)); diff --git a/src/CLI/Test/Monai.Deploy.InformaticsGateway.CLI.Test.csproj b/src/CLI/Test/Monai.Deploy.InformaticsGateway.CLI.Test.csproj index 53f43febb..48f99fe83 100644 --- a/src/CLI/Test/Monai.Deploy.InformaticsGateway.CLI.Test.csproj +++ b/src/CLI/Test/Monai.Deploy.InformaticsGateway.CLI.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.CLI.Test Apache-2.0 false true - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + \ No newline at end of file diff --git a/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs b/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs new file mode 100644 index 000000000..4da277490 --- /dev/null +++ b/src/CLI/Test/NLogConfigurationOptionAccessorTest.cs @@ -0,0 +1,55 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; +using Monai.Deploy.InformaticsGateway.CLI.Services; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.CLI.Test +{ + public class NLogConfigurationOptionAccessorTest + { + public NLogConfigurationOptionAccessorTest() + { + } + + [Fact(DisplayName = "NLogConfigurationOptionAccessor Constructor")] + public void DockerRunner_Constructor() + { + Assert.Throws(() => new NLogConfigurationOptionAccessor(null)); + } + + [Fact] + public void DicomListeningPort_Get_ReturnsValue() + { + var fileSystem = SetupFileSystem(); + var configurationOptionAccessor = new NLogConfigurationOptionAccessor(fileSystem); + + Assert.Equal($"{Common.ContainerApplicationRootPath}/logs/", configurationOptionAccessor.LogStoragePath); + } + + private IFileSystem SetupFileSystem() + { + return new MockFileSystem(new Dictionary + { + {Common.NLogConfigFilePath, new MockFileData("\r\n \r\n") } + }); + } + } +} diff --git a/src/CLI/Test/SourceCommandTest.cs b/src/CLI/Test/SourceCommandTest.cs index e4d071bca..edbca5105 100644 --- a/src/CLI/Test/SourceCommandTest.cs +++ b/src/CLI/Test/SourceCommandTest.cs @@ -346,6 +346,5 @@ public async Task SrcUpdate_Command_ConfigurationException() _logger.VerifyLoggingMessageBeginsWith("Please execute `testhost config init` to intialize Informatics Gateway.", LogLevel.Critical, Times.Once()); } - } } diff --git a/src/CLI/Test/packages.lock.json b/src/CLI/Test/packages.lock.json index ddab38695..8a940a817 100644 --- a/src/CLI/Test/packages.lock.json +++ b/src/CLI/Test/packages.lock.json @@ -1,30 +1,30 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "System.CommandLine.Hosting": { @@ -40,68 +40,70 @@ }, "System.IO.Abstractions.TestingHelpers": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "tkXvQbsfOIfeoGso+WptCuouFLiWt3EU8s0D8poqIVz1BJOOszkPuFbFgP2HUTJ9bp5n1HH89eFHILo6Oz5XUw==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", "dependencies": { - "System.IO.Abstractions": "17.2.3" + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" } }, "xRetry": { "type": "Direct", - "requested": "[1.8.0, )", - "resolved": "1.8.0", - "contentHash": "H8KXWHBjQASwD4y/7L2j7j4KLmg8z4+mCV4atrhZvJVnCkVSKLkWe1lfKGmaCYkKt2dJnC4yH+tJXGqthSkGGg==", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", "dependencies": { "xunit.core": "[2.4.0, 3.0.0)" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "Crayon": { "type": "Transitive", "resolved": "2.0.69", @@ -109,8 +111,8 @@ }, "Docker.DotNet": { "type": "Transitive", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", @@ -119,48 +121,46 @@ }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.Bcl.AsyncInterfaces": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.CSharp": { "type": "Transitive", @@ -169,32 +169,32 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.CommandLine": { @@ -208,8 +208,8 @@ }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", "dependencies": { "Microsoft.Extensions.Configuration": "6.0.0", "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" @@ -241,8 +241,8 @@ }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", "Microsoft.Extensions.Configuration.Json": "6.0.0", @@ -252,40 +252,58 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "8.0.0", + "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { @@ -305,17 +323,17 @@ }, "Microsoft.Extensions.Hosting": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", + "resolved": "6.0.0", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", "dependencies": { "Microsoft.Extensions.Configuration": "6.0.0", "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", "Microsoft.Extensions.Configuration.Binder": "6.0.0", "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", "Microsoft.Extensions.DependencyInjection": "6.0.0", "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", @@ -333,41 +351,46 @@ }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Http": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.0", + "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -435,296 +458,113 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Newtonsoft.Json.Bson": { + "Polly": { "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" + "Polly.Core": "8.5.2" } }, - "NuGet.Frameworks": { + "Polly.Core": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -732,33 +572,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.CommandLine": { "type": "Transitive", "resolved": "2.0.0-beta4.22272.1", @@ -781,728 +594,80 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Linq.Expressions": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, - "System.Threading.Timer": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Xml.ReaderWriter": { + "TestableIO.System.IO.Abstractions.TestingHelpers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Xml.XDocument": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1512,94 +677,83 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "mig-cli": { "type": "Project", "dependencies": { - "Crayon": "2.0.69", - "Docker.DotNet": "3.125.12", - "Microsoft.Extensions.Hosting": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Client": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "System.CommandLine": "2.0.0-beta4.22272.1", - "System.CommandLine.Hosting": "0.4.0-alpha.22272.1", - "System.CommandLine.Rendering": "0.4.0-alpha.22272.1", - "System.IO.Abstractions": "17.2.3" + "Crayon": "[2.0.69, )", + "Docker.DotNet": "[3.125.15, )", + "Microsoft.Extensions.Http": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Client": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "System.CommandLine.Hosting": "[0.4.0-alpha.22272.1, )", + "System.CommandLine.Rendering": "[0.4.0-alpha.22272.1, )" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client": { "type": "Project", "dependencies": { - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/CLI/packages.lock.json b/src/CLI/packages.lock.json index 09ee1a763..1d663fee2 100644 --- a/src/CLI/packages.lock.json +++ b/src/CLI/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Crayon": { "type": "Direct", "requested": "[2.0.69, )", @@ -10,82 +10,34 @@ }, "Docker.DotNet": { "type": "Direct", - "requested": "[3.125.12, )", - "resolved": "3.125.12", - "contentHash": "lkDh6PUV8SIM1swPCkd3f+8zGB7Z9Am3C2XBLqAjyIIp5jQBCsDFrtbtA1QiVdFMWdYcJscrX/gzzG50kRj0jQ==", + "requested": "[3.125.15, )", + "resolved": "3.125.15", + "contentHash": "XN8FKxVv8Mjmwu104/Hl9lM61pLY675s70gzwSj8KR5pwblo8HfWLcCuinh9kYsqujBkMH4HVRCEcRuU6al4BQ==", "dependencies": { "Newtonsoft.Json": "13.0.1", "System.Buffers": "4.5.1", "System.Threading.Tasks.Extensions": "4.5.4" } }, - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "Microsoft.Extensions.Hosting": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Logging.Debug": "6.0.0", - "Microsoft.Extensions.Logging.EventLog": "6.0.0", - "Microsoft.Extensions.Logging.EventSource": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } - }, - "Microsoft.Extensions.Logging": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } - }, - "Microsoft.Extensions.Logging.Console": { + "Microsoft.Extensions.Http": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" } }, - "System.CommandLine": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[2.0.0-beta4.22272.1, )", - "resolved": "2.0.0-beta4.22272.1", - "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "System.CommandLine.Hosting": { "type": "Direct", @@ -108,72 +60,66 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.IO.Abstractions": { - "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.Bcl.AsyncInterfaces": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.CSharp": { "type": "Transitive", @@ -182,32 +128,32 @@ }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.CommandLine": { @@ -221,8 +167,8 @@ }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "resolved": "6.0.0", + "contentHash": "DjYkzqvhiHCq38LW71PcIxXk6nhtV6VySP9yDcSO0goPl7YCU1VG1f2Wbgy58lkA10pWkjHCblZPUyboCB93ZA==", "dependencies": { "Microsoft.Extensions.Configuration": "6.0.0", "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" @@ -254,8 +200,8 @@ }, "Microsoft.Extensions.Configuration.UserSecrets": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "resolved": "6.0.0", + "contentHash": "lB0Hb2V4+RUHy+LjEcqEr4EcV4RWc9EnjAV2GdtWQEdljQX+R4hGREftI7sInU9okP93pDrJiaj6QUJ6ZsslOA==", "dependencies": { "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", "Microsoft.Extensions.Configuration.Json": "6.0.0", @@ -265,40 +211,58 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "8.0.0", + "contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3PZp/YSkIXrF7QK7PfC1bkyRYwqOHpWFad8Qx+4wkuumAeXo1NHaxpS9LboNA9OvNSAu+QOVlXbMyoY+pHSqcw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { @@ -316,31 +280,63 @@ "resolved": "6.0.0", "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" }, - "Microsoft.Extensions.Hosting.Abstractions": { + "Microsoft.Extensions.Hosting": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "contentHash": "M8VzD0ni5VarIRT8njnwK4K2WSAo0kZH4Zc3mKcSGkP4CjDZ91T9ZEFmmwhmo4z7x8AFq+tW0WFi9wX+K2cxkQ==", "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.0", "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", + "Microsoft.Extensions.Configuration.Binder": "6.0.0", + "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", + "Microsoft.Extensions.Configuration.Json": "6.0.0", + "Microsoft.Extensions.Configuration.UserSecrets": "6.0.0", + "Microsoft.Extensions.DependencyInjection": "6.0.0", "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", + "Microsoft.Extensions.FileProviders.Physical": "6.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Logging.Console": "6.0.0", + "Microsoft.Extensions.Logging.Debug": "6.0.0", + "Microsoft.Extensions.Logging.EventLog": "6.0.0", + "Microsoft.Extensions.Logging.EventSource": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" } }, - "Microsoft.Extensions.Http": { + "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", @@ -357,6 +353,19 @@ "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" } }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Configuration": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Text.Json": "6.0.0" + } + }, "Microsoft.Extensions.Logging.Debug": { "type": "Transitive", "resolved": "6.0.0", @@ -395,273 +404,96 @@ }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Polly": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "Polly.Core": "8.5.2" } }, - "runtime.native.System.IO.Compression": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Security.Cryptography.Apple": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -669,32 +501,10 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { + "System.CommandLine": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "2.0.0-beta4.22272.1", + "contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==" }, "System.CommandLine.NamingConventionBinder": { "type": "Transitive", @@ -704,753 +514,99 @@ "System.CommandLine": "2.0.0-beta4.22272.1" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { + "System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Runtime": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, - "System.Threading.Timer": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Xml.XDocument": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client": { "type": "Project", "dependencies": { - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/Client.Common/Monai.Deploy.InformaticsGateway.Client.Common.csproj b/src/Client.Common/Monai.Deploy.InformaticsGateway.Client.Common.csproj index 1d7713966..4691b8446 100644 --- a/src/Client.Common/Monai.Deploy.InformaticsGateway.Client.Common.csproj +++ b/src/Client.Common/Monai.Deploy.InformaticsGateway.Client.Common.csproj @@ -13,35 +13,29 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Client.Common Apache-2.0 true True ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - - - All - - + - - - + + + diff --git a/src/Client.Common/ProblemDetails.cs b/src/Client.Common/ProblemDetails.cs index 4a37cdea3..c42e54192 100644 --- a/src/Client.Common/ProblemDetails.cs +++ b/src/Client.Common/ProblemDetails.cs @@ -15,11 +15,8 @@ * limitations under the License. */ -using System; - namespace Monai.Deploy.InformaticsGateway.Client.Common { - [Serializable] public class ProblemDetails { public string Title { get; set; } diff --git a/src/Client.Common/ProblemException.cs b/src/Client.Common/ProblemException.cs index 81989b0f7..6eb9145c1 100644 --- a/src/Client.Common/ProblemException.cs +++ b/src/Client.Common/ProblemException.cs @@ -16,40 +16,21 @@ */ using System; -using System.Runtime.Serialization; using Ardalis.GuardClauses; namespace Monai.Deploy.InformaticsGateway.Client.Common { - [Serializable] public class ProblemException : Exception { public ProblemDetails ProblemDetails { get; private set; } public ProblemException(ProblemDetails problemDetails) : base(problemDetails?.Detail) { - Guard.Against.Null(problemDetails); + Guard.Against.Null(problemDetails, nameof(problemDetails)); ProblemDetails = problemDetails; } - protected ProblemException(SerializationInfo info, StreamingContext context) : base(info, context) - { - ProblemDetails = (ProblemDetails)info.GetValue(nameof(ProblemDetails), typeof(ProblemDetails)); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - info.AddValue(nameof(ProblemDetails), ProblemDetails, typeof(ProblemDetails)); - - base.GetObjectData(info, context); - } - public override string Message => ToString(); public override string ToString() diff --git a/src/Client.Common/Test/Monai.Deploy.InformaticsGateway.Client.Common.Test.csproj b/src/Client.Common/Test/Monai.Deploy.InformaticsGateway.Client.Common.Test.csproj index a587df6c5..0ab49cc1e 100644 --- a/src/Client.Common/Test/Monai.Deploy.InformaticsGateway.Client.Common.Test.csproj +++ b/src/Client.Common/Test/Monai.Deploy.InformaticsGateway.Client.Common.Test.csproj @@ -1,5 +1,5 @@ - - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test Apache-2.0 false true - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - + \ No newline at end of file diff --git a/src/Client.Common/Test/ProblemExceptionTest.cs b/src/Client.Common/Test/ProblemExceptionTest.cs deleted file mode 100644 index ab72e888e..000000000 --- a/src/Client.Common/Test/ProblemExceptionTest.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2022 MONAI Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using Monai.Deploy.InformaticsGateway.Client.Common; -using Xunit; - -namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test -{ - public class ProblemExceptionTest - { - [Fact] - public void TestProblemExceptionSerialization() - { - var exception = new ProblemException(new ProblemDetails - { - Detail = "details", - Title = "title", - Status = 100 - }); - - var data = SerializeToBytes(exception); - var result = DeserializeFromBytes(data); - - Assert.Equal(exception.ProblemDetails.Detail, result.ProblemDetails.Detail); - Assert.Equal(exception.ProblemDetails.Title, result.ProblemDetails.Title); - Assert.Equal(exception.ProblemDetails.Status, result.ProblemDetails.Status); - Assert.Equal(exception.Message, result.Message); - } - - private static byte[] SerializeToBytes(T e) - { - using var stream = new MemoryStream(); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - new BinaryFormatter().Serialize(stream, e); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - return stream.GetBuffer(); - } - - private static T DeserializeFromBytes(byte[] bytes) - { - using var stream = new MemoryStream(bytes); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - return (T)new BinaryFormatter().Deserialize(stream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - } - } -} diff --git a/src/Client.Common/Test/packages.lock.json b/src/Client.Common/Test/packages.lock.json index a5cca0241..7e2db4a2f 100644 --- a/src/Client.Common/Test/packages.lock.json +++ b/src/Client.Common/Test/packages.lock.json @@ -1,1043 +1,109 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Ardalis.GuardClauses": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "xRetry": { "type": "Direct", - "requested": "[1.8.0, )", - "resolved": "1.8.0", - "contentHash": "H8KXWHBjQASwD4y/7L2j7j4KLmg8z4+mCV4atrhZvJVnCkVSKLkWe1lfKGmaCYkKt2dJnC4yH+tJXGqthSkGGg==", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", "dependencies": { "xunit.core": "[2.4.0, 3.0.0)" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" - }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Win32.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ratu44uTIHgeBeI0dE8DWvmXVBSo4u7ozRZZHOMmK/JPpYyo0dAfgSiHlpiObMQ5lEtEyIXA40sKRYg5J6A8uQ==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -1045,49 +111,43 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } } } diff --git a/src/Client.Common/UriExtensions.cs b/src/Client.Common/UriExtensions.cs old mode 100644 new mode 100755 index d22e6d6e8..ebd7f2ff3 --- a/src/Client.Common/UriExtensions.cs +++ b/src/Client.Common/UriExtensions.cs @@ -28,7 +28,7 @@ public static Uri EnsureUriEndsWithSlash(this Uri input) var str = input.ToString(); - if (!string.IsNullOrWhiteSpace(str) && !str.EndsWith("/")) + if (!string.IsNullOrWhiteSpace(str) && !str.EndsWith('/')) { #pragma warning disable S1075 // URIs should not be hardcoded return new Uri(str + '/'); diff --git a/src/Client.Common/packages.lock.json b/src/Client.Common/packages.lock.json index 2b8dfa262..9ce395052 100644 --- a/src/Client.Common/packages.lock.json +++ b/src/Client.Common/packages.lock.json @@ -1,49 +1,18 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Ardalis.GuardClauses": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, - "GitVersion.MsBuild": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "System.Text.Json": { - "type": "Direct", - "requested": "[6.0.7, )", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" } } } diff --git a/src/Client/Configuration.cs b/src/Client/Configuration.cs index 9927c087c..ab2634210 100644 --- a/src/Client/Configuration.cs +++ b/src/Client/Configuration.cs @@ -32,7 +32,6 @@ static Configuration() JsonSerializationOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString; JsonSerializationOptions.WriteIndented = false; JsonSerializationOptions.Converters.Add(new JsonStringEnumMemberConverter(JsonNamingPolicy.CamelCase, false)); - JsonSerializationOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false)); } } } diff --git a/src/Client/HttpContentExtensions.cs b/src/Client/HttpContentExtensions.cs new file mode 100644 index 000000000..826ed972a --- /dev/null +++ b/src/Client/HttpContentExtensions.cs @@ -0,0 +1,33 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Net.Http; +using System.Text.Json; +using System.Threading; + +namespace Monai.Deploy.InformaticsGateway.Client +{ + internal static class HttpContentExtensions + { + public static async System.Threading.Tasks.Task ReadAsAsync(this HttpContent httpContent, CancellationToken cancellationToken) + { + using (var contentStream = await httpContent.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false)) + { + return await JsonSerializer.DeserializeAsync(contentStream, Configuration.JsonSerializationOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + } + } + } +} diff --git a/src/Client/IInformaticsGatewayClient.cs b/src/Client/IInformaticsGatewayClient.cs old mode 100644 new mode 100755 index 38ebb2bc6..3db89516e --- a/src/Client/IInformaticsGatewayClient.cs +++ b/src/Client/IInformaticsGatewayClient.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ using System; using System.Net.Http.Headers; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Client.Services; namespace Monai.Deploy.InformaticsGateway.Client @@ -48,6 +49,16 @@ public interface IInformaticsGatewayClient /// IAeTitleService DicomDestinations { get; } + /// + /// Provides APIs to list, create, delete Virtual AE Titles. + /// + IAeTitleService VirtualAeTitle { get; } + + /// + /// Provides APIs to list, create, delete Virtual AE Titles. + /// + IAeTitleService HL7Destinations { get; } + /// /// Configures the service URI of the DICOMweb service. /// diff --git a/src/Client/InformaticsGatewayClient.cs b/src/Client/InformaticsGatewayClient.cs old mode 100644 new mode 100755 index 3884fb940..fe9a81145 --- a/src/Client/InformaticsGatewayClient.cs +++ b/src/Client/InformaticsGatewayClient.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.Client.Services; @@ -45,6 +46,12 @@ public class InformaticsGatewayClient : IInformaticsGatewayClient /// public IAeTitleService DicomDestinations { get; } + /// + public IAeTitleService VirtualAeTitle { get; } + + /// + public IAeTitleService HL7Destinations { get; } + /// /// Initializes a new instance of the InformaticsGatewayClient class that connects to the specified URI using the credentials provided. /// @@ -62,6 +69,8 @@ public InformaticsGatewayClient(HttpClient httpClient, ILogger("config/ae", _httpClient, _logger); DicomSources = new AeTitleService("config/source", _httpClient, _logger); DicomDestinations = new AeTitleService("config/destination", _httpClient, _logger); + VirtualAeTitle = new AeTitleService("config/vae", _httpClient, _logger); + HL7Destinations = new AeTitleService("config/hl7-destination", _httpClient, _logger); } /// diff --git a/src/Client/AssemblyInfo.cs b/src/Client/InternalVisibleTo.cs similarity index 100% rename from src/Client/AssemblyInfo.cs rename to src/Client/InternalVisibleTo.cs diff --git a/src/Client/Monai.Deploy.InformaticsGateway.Client.csproj b/src/Client/Monai.Deploy.InformaticsGateway.Client.csproj index ea2ce1a6f..8707615f7 100644 --- a/src/Client/Monai.Deploy.InformaticsGateway.Client.csproj +++ b/src/Client/Monai.Deploy.InformaticsGateway.Client.csproj @@ -13,37 +13,31 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Client Apache-2.0 ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - - - All - - - - - - - + + + + + + - + \ No newline at end of file diff --git a/src/Client/Services/AeTitle{T}Service.cs b/src/Client/Services/AeTitle{T}Service.cs old mode 100644 new mode 100755 index e804cc1be..e9df749ec --- a/src/Client/Services/AeTitle{T}Service.cs +++ b/src/Client/Services/AeTitle{T}Service.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ using System.Threading.Tasks; using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Client.Services { @@ -40,6 +40,8 @@ public interface IAeTitleService Task Delete(string aeTitle, CancellationToken cancellationToken); Task CEcho(string name, CancellationToken cancellationToken); + + Task> PlugIns(CancellationToken cancellationToken); } internal class AeTitleService : ServiceBase, IAeTitleService @@ -49,15 +51,15 @@ internal class AeTitleService : ServiceBase, IAeTitleService public AeTitleService(string route, HttpClient httpClient, ILogger logger = null) : base(httpClient, logger) { - Guard.Against.NullOrWhiteSpace(route); - Guard.Against.Null(httpClient); + Guard.Against.NullOrWhiteSpace(route, nameof(route)); + Guard.Against.Null(httpClient, nameof(httpClient)); Route = route; } public async Task Create(T item, CancellationToken cancellationToken) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); Logger.SendingRequestTo(Route); var response = await HttpClient.PostAsJsonAsync(Route, item, Configuration.JsonSerializationOptions, cancellationToken).ConfigureAwait(false); @@ -68,7 +70,7 @@ public async Task Create(T item, CancellationToken cancellationToken) public async Task Delete(string aeTitle, CancellationToken cancellationToken) { aeTitle = Uri.EscapeDataString(aeTitle); - Guard.Against.NullOrWhiteSpace(aeTitle); + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); Logger.SendingRequestTo($"{Route}/{aeTitle}"); var response = await HttpClient.DeleteAsync($"{Route}/{aeTitle}", cancellationToken).ConfigureAwait(false); await response.EnsureSuccessStatusCodeWithProblemDetails(Logger).ConfigureAwait(false); @@ -78,7 +80,7 @@ public async Task Delete(string aeTitle, CancellationToken cancellationToken) public async Task GetAeTitle(string aeTitle, CancellationToken cancellationToken) { aeTitle = Uri.EscapeDataString(aeTitle); - Guard.Against.NullOrWhiteSpace(aeTitle); + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); Logger.SendingRequestTo($"{Route}/{aeTitle}"); var response = await HttpClient.GetAsync($"{Route}/{aeTitle}", cancellationToken).ConfigureAwait(false); await response.EnsureSuccessStatusCodeWithProblemDetails(Logger).ConfigureAwait(false); @@ -101,7 +103,7 @@ public async Task CEcho(string name, CancellationToken cancellationToken) throw new NotSupportedException($"C-ECHO is not supported for {typeof(T).Name}"); } name = Uri.EscapeDataString(name); - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); Logger.SendingRequestTo($"{Route}/{name}"); var response = await HttpClient.GetAsync($"{Route}/cecho/{name}", cancellationToken).ConfigureAwait(false); await response.EnsureSuccessStatusCodeWithProblemDetails(Logger).ConfigureAwait(false); @@ -109,12 +111,28 @@ public async Task CEcho(string name, CancellationToken cancellationToken) public async Task Update(T item, CancellationToken cancellationToken) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); Logger.SendingRequestTo(Route); var response = await HttpClient.PutAsJsonAsync(Route, item, Configuration.JsonSerializationOptions, cancellationToken).ConfigureAwait(false); await response.EnsureSuccessStatusCodeWithProblemDetails(Logger).ConfigureAwait(false); return await response.Content.ReadAsAsync(cancellationToken).ConfigureAwait(false); } + + public async Task> PlugIns(CancellationToken cancellationToken) + { + if (typeof(T) != typeof(MonaiApplicationEntity) && + typeof(T) != typeof(DestinationApplicationEntity)) + { + throw new NotSupportedException($"Plug-ins API is not available for {typeof(T).Name}"); + } + + var route = $"{Route}/plug-ins"; + Logger.SendingRequestTo(route); + var response = await HttpClient.GetAsync(route, cancellationToken).ConfigureAwait(false); + await response.EnsureSuccessStatusCodeWithProblemDetails(Logger).ConfigureAwait(false); + var result = await response.Content.ReadFromJsonAsync>(Configuration.JsonSerializationOptions, cancellationToken).ConfigureAwait(false); + return result; + } } } diff --git a/src/Client/Services/HealthService.cs b/src/Client/Services/HealthService.cs index 7b7a5b4ab..1d9be5472 100644 --- a/src/Client/Services/HealthService.cs +++ b/src/Client/Services/HealthService.cs @@ -39,7 +39,7 @@ internal class HealthService : ServiceBase, IHealthService public HealthService(HttpClient httpClient, ILogger logger = null) : base(httpClient, logger) { - Guard.Against.Null(httpClient); + Guard.Against.Null(httpClient, nameof(httpClient)); } public async Task Live(CancellationToken cancellationToken) => await LiveReady("live", cancellationToken).ConfigureAwait(false); diff --git a/src/Client/Test/AeTitleServiceTest.cs b/src/Client/Test/AeTitleServiceTest.cs old mode 100644 new mode 100755 index ef68418bd..df852a205 --- a/src/Client/Test/AeTitleServiceTest.cs +++ b/src/Client/Test/AeTitleServiceTest.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.Client.Services; using Moq; @@ -422,7 +423,6 @@ public async Task CEcho() var exception = await Record.ExceptionAsync(async () => await service.CEcho(aet.Name, CancellationToken.None)); Assert.Null(exception); - } [Fact(DisplayName = "Destination - C-ECHO returns a problem")] @@ -461,5 +461,64 @@ public async Task CEcho_ReturnsAProblem() Assert.Equal($"HTTP Status: {problem.Status}. {problem.Detail}", result.Message); } + + [Fact(DisplayName = "AET - Plug-ins")] + public async Task PlugIns() + { + var plugins = new Dictionary + { + {"A","1" }, + {"B","2" }, + {"C","3" }, + }; + + var json = JsonSerializer.Serialize(plugins, Configuration.JsonSerializationOptions); + + var rootUri = new Uri("http://localhost:5000"); + var uriPath = "config/monaiaetitle/plug-ins"; + + var httpResponse = new HttpResponseMessage + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(json, Encoding.UTF8, "application/json") + }; + + var httpClient = SetupHttpClientMock(rootUri, HttpMethod.Get, httpResponse); + + var service = new AeTitleService(uriPath, httpClient, _logger.Object); + + var exception = await Record.ExceptionAsync(async () => await service.PlugIns(CancellationToken.None)); + Assert.Null(exception); + } + + [Fact(DisplayName = "AET - Plug-ins returns a problem")] + public async Task PlugIns_ReturnsAProblem() + { + var problem = new ProblemDetails + { + Title = "Problem Title", + Detail = "Problem Detail", + Status = 500 + }; + + var json = JsonSerializer.Serialize(problem, Configuration.JsonSerializationOptions); + + var rootUri = new Uri("http://localhost:5000"); + var uriPath = "config/destination"; + + var httpResponse = new HttpResponseMessage + { + StatusCode = HttpStatusCode.InternalServerError, + Content = new StringContent(json, Encoding.UTF8, "application/json") + }; + + var httpClient = SetupHttpClientMock(rootUri, HttpMethod.Get, httpResponse); + + var service = new AeTitleService(uriPath, httpClient, _logger.Object); + + var result = await Assert.ThrowsAsync(async () => await service.PlugIns(CancellationToken.None)); + + Assert.Equal($"HTTP Status: {problem.Status}. {problem.Detail}", result.Message); + } } } diff --git a/src/Client/Test/HttpResponseMessageExtensionsTest.cs b/src/Client/Test/HttpResponseMessageExtensionsTest.cs index a4add5758..7fdc06114 100644 --- a/src/Client/Test/HttpResponseMessageExtensionsTest.cs +++ b/src/Client/Test/HttpResponseMessageExtensionsTest.cs @@ -35,7 +35,7 @@ public class HttpResponseMessageExtensionsTest public async Task SuccessStatusCode() { var message = new HttpResponseMessage(HttpStatusCode.OK); - var exception = await Record.ExceptionAsync(async () => await message.EnsureSuccessStatusCodeWithProblemDetails()).ConfigureAwait(false); + var exception = await Record.ExceptionAsync(async () => await message.EnsureSuccessStatusCodeWithProblemDetails()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(exception); } diff --git a/src/Client/Test/Monai.Deploy.InformaticsGateway.Client.Test.csproj b/src/Client/Test/Monai.Deploy.InformaticsGateway.Client.Test.csproj index 91c4bb66a..be84c02f4 100644 --- a/src/Client/Test/Monai.Deploy.InformaticsGateway.Client.Test.csproj +++ b/src/Client/Test/Monai.Deploy.InformaticsGateway.Client.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Client.Test Apache-2.0 false true - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - + \ No newline at end of file diff --git a/src/Client/Test/packages.lock.json b/src/Client/Test/packages.lock.json index ce13c18af..6d439ef64 100644 --- a/src/Client/Test/packages.lock.json +++ b/src/Client/Test/packages.lock.json @@ -1,85 +1,88 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AspNetCore.HealthChecks.MongoDb": { + "type": "Transitive", + "resolved": "8.1.0", + "contentHash": "NuVDrj7UXBVniePh6JnuM8ryZRWdOIGOBes3owg2WQV/1NPntpWqX/DYaP6SBduHULUp8XRbwAui8qKQAW4SDA==", "dependencies": { - "JetBrains.Annotations": "2021.3.0" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "MongoDB.Driver": "2.28.0" } }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, - "Crc32.NET": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", - "dependencies": { - "NETStandard.Library": "2.0.0" - } + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, "DnsClient": { "type": "Transitive", @@ -91,154 +94,211 @@ }, "DotNext": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "5Xp6G9U0MhSmfgxKklUUsOFfSg2VqF+/rkd7WyoUs7HqbnVd32bRw2rWW5o+rieHLzUlW/sagctPiaZqmeTA+g==", + "resolved": "5.3.1", + "contentHash": "PIkzfT1wsvWGXb4Zigpfb7kUfNQdmlKi57yXYeMRE+06/JzpruwrBnQvwtKGw0qgn9lM4EIXyVfo4gnUARFDRw==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.IO.Hashing": "8.0.0" } }, "DotNext.Threading": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "G/AogSunqiZZ/0H4y3Qy/YNveIB+6azddStmFxbxLWkruXZ27gXyoRQ9kQ2gpDbq/+YfMINz9nmTY5ZtuCzuyw==", + "resolved": "5.5.0", + "contentHash": "djd6MPTZA3n2VcpViD0D03tkReudsCWpAefIhiRQCWEgTcW0uW1cZqlTh7qX15Jd5sKBE9qB13mJsrchpu3MKQ==", "dependencies": { - "DotNext": "4.7.4", - "System.Threading.Channels": "6.0.0" + "DotNext": "5.3.1", + "System.Threading.Channels": "8.0.0" } }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "fo-dicom.NLog": { + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Macross.Json.Extensions": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + }, + "Microsoft.AspNetCore.Authentication.JwtBearer": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "k35FD+C9IcpTLjCF5tvCkBGUxJ+YvzoBsgb2VAtGQv+aVTu+HyoCnNVqccc4lVE53fbVCwpR3gPiTAnm5fm+KQ==", + "resolved": "8.0.14", + "contentHash": "e19jmWJAQucbPYk3/fihJMDCYfv6CO+Qwp34pOehUSCbaHROw6FZ551SN1D0s9Btl0U/QHfuwFq6Z8Oa2ktE6g==", "dependencies": { - "NLog": "4.7.11", - "fo-dicom": "5.0.3" + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2" } }, - "HL7-dotnetcore": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "2.29.0", - "contentHash": "E0N/W72HsvIJj6XGyiUv9BHmyhvkNedpa23QN/Xwk47S965NYC9JSA1VVYWAAs4J6yOIhpM3lBOEWvhQBO31Lw==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "JetBrains.Annotations": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Karambolo.Extensions.Logging.File": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "3.3.1", - "contentHash": "wkPTc/UEuSAwbO3/Ee+oCdotxncmc/DKwjM533Z0BKvJm94NLOvU2i7pifgMd6uAUJ8jy69OcFZRu7hXKbMW6g==", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", "dependencies": { - "Microsoft.Extensions.FileProviders.Physical": "3.0.0", - "Microsoft.Extensions.Logging.Configuration": "3.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "3.0.0", - "System.Threading.Channels": "4.7.1" + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" } }, - "Macross.Json.Extensions": { + "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" } }, - "Microsoft.Bcl.AsyncInterfaces": { + "Microsoft.CodeAnalysis.Workspaces.Common": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, - "Microsoft.CodeCoverage": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.CSharp": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "nWlJaFEvT7HpLkOusyReeQPeHsl5EUIBlI5Pr05SYhkWFQ7KGLmUNdv4j1aAjrO+x42Enqr7fu7r3MXLPQqImg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Design": "8.0.14" } }, "Microsoft.Extensions.ApiDescription.Server": { @@ -248,343 +308,264 @@ }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.CommandLine": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.UserSecrets": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "E6HxKQvrm0AeDagW6w+CsyVfXAO/pscrbX6mQ+XnThdwkeTxi0cnuXDTiTmd+WSmofSfpBKOS0VlvHUOxskdLQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "MQS7GE1ux7Lo1yOr59M7ZTEoFY3GJ9hHkxXQnQc8EPxkt5S7cX4qe6djSWH+mk9qQan+AjFZzdC1x5Af5IaseA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "tMjF1erFhHE+SnsiIyRIatVeKBgB9OfGsOvQe/+foE0xl4+JUQGbCA7gF1wqOksi9AxmGWzqtqjsMKJpCo5wYQ==", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11" + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" - }, - "Microsoft.Extensions.Hosting": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Logging.Debug": "6.0.0", - "Microsoft.Extensions.Logging.EventLog": "6.0.0", - "Microsoft.Extensions.Logging.EventSource": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Http": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, - "Microsoft.Extensions.Logging.Console": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.Debug": { + "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.EventLog": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.EventLog": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Extensions.Logging.EventSource": { + "Microsoft.IdentityModel.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "resolved": "7.1.2", + "contentHash": "33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Options": { + "Microsoft.IdentityModel.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.1.2", + "contentHash": "YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Abstractions": "7.1.2" } }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { + "Microsoft.IdentityModel.Protocols": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "7.1.2", + "contentHash": "SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Logging": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "7.1.2", + "contentHash": "6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.IdentityModel.Protocols": "7.1.2", + "System.IdentityModel.Tokens.Jwt": "7.1.2" } }, - "Microsoft.Net.Http.Headers": { + "Microsoft.IdentityModel.Tokens": { "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", + "resolved": "7.1.2", + "contentHash": "oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==", "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" + "Microsoft.IdentityModel.Logging": "7.1.2" } }, "Microsoft.NETCore.Platforms": { @@ -592,39 +573,28 @@ "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.3", - "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" - }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "1.2.3", - "contentHash": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==" + "resolved": "1.6.23", + "contentHash": "tZ1I0KXnn98CWuV8cpI247A17jaY+ILS9vvF7yhI0uPPEqF4P1d7BWL5Uwtel10w9NucllHB3nTkfYTAcHAh8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -636,281 +606,180 @@ }, "Minio": { "type": "Transitive", - "resolved": "4.0.6", - "contentHash": "c/7hO1I0/PB1hJr7HXuNxsTT59xi4ADPxHhGtv7zzTNZqDqq+Vgv+C8xSJ6rlIy4px7ibhMt6Kunq20ZBlLj4Q==", + "resolved": "6.0.2", + "contentHash": "4Od4uGANX5X0AL90WV0viBNzpE2+jDHro6CGvR4//MVj5SiTTwR5SUikXgd/2G2PtYyXw4b/IBpo7Kt5cCCndA==", "dependencies": { - "Crc32.NET": "1.2.0", - "Microsoft.CSharp": "4.7.0", - "Newtonsoft.Json": "13.0.1", - "System.Net.Http": "4.3.4", - "System.Net.Primitives": "4.3.1", - "System.Reactive.Linq": "5.0.0", - "System.ValueTuple": "4.4.0" + "CommunityToolkit.HighPerformance": "8.2.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "System.IO.Hashing": "8.0.0", + "System.Reactive": "6.0.0" } }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "P7JlaepxiuUxY3GmKhKnhkDKaHID5KFEE//fIwUkhS39WMYLzq3K63URzM/8EIPmuZQnxl2n7EEZVxafY4FHQg==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" + } + }, + "Monai.Deploy.Security": { + "type": "Transitive", + "resolved": "1.0.1", + "contentHash": "GrGj/addv+V5MgZuHBXV68M9PtyzrPlvHibyaHPQq602wtK4CYqPGR5P4VJgM2ZuiW89vEKbhd07sxTFkG9Muw==", "dependencies": { - "Monai.Deploy.Messaging": "0.1.16", - "Polly": "7.2.3", - "RabbitMQ.Client": "6.4.0", - "System.Collections.Concurrent": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.14", + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Logging.Configuration": "8.0.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.MinIO": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "k6j9u4x0gcMml2b5wUufItGvA8YqDnw7utxjsG1wSF7lGsqs5R5ajr9m6Z9//fSzFQ7bn1iMexnDLDhLLmo+eQ==", + "resolved": "1.0.2", + "contentHash": "H73iUS64UwZXK8ZxNUc3DvokEcptpcoYgY+FMJGlotCffGhqrK8iJ18THR0gg9Io5UgIv24mdu1jbUn/Cz2rhA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Minio": "4.0.6", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.S3Policy": "0.2.10" + "Minio": "6.0.2", + "Monai.Deploy.Storage": "1.0.2", + "Monai.Deploy.Storage.S3Policy": "1.0.2" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" }, - "NETStandard.Library": { + "Mono.TextTemplating": { "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, "NLog": { "type": "Transitive", - "resolved": "5.0.5", - "contentHash": "NOTWSyUEljmjMq7OqZ1X9iu4bJ+rW/o6pt79Jq8j2Q7s8DyoMMCJwe0HoCKcNjhYRJ++b+E8erH6E6WwaCTshQ==" + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "NLog.Extensions.Logging": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "cQCKF2/iYjZUkn0d2o6VD1xkTUhIFHPYmZEm29KlTthLEzMht5aY80SwWlHZCKy0w19kaSq1jgLJSGrKsapUfg==", + "resolved": "5.4.0", + "contentHash": "5T19INfbzRywZpyBGoQChsB/R7eHW9hR4Ml9O22NRjJpfltGIJ+rsoCcjwr2/V/E6i3/eXLTQwRAeDMICjWpTA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "NLog": "5.0.5" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "NLog": "5.4.0" } }, "NLog.Web.AspNetCore": { "type": "Transitive", - "resolved": "5.1.5", - "contentHash": "a7Pe6KdwIxPxcFYy6M7wLseU++tx1bBf/ROVNlcZPLfp40DPLA0KGOk1K9kvbcwPYKKLMikdSwiOyTRZZFlaXg==", + "resolved": "5.4.0", + "contentHash": "Le6rbipSqeGVygbLGgRwZNMsbjJ+YnoAMJdZVhgjQimXwmYbDNkSswK6Mb32bKoC5aIg5mhw+hpAmgWdegs4Eg==", "dependencies": { - "NLog.Extensions.Logging": "5.1.0" + "NLog.Extensions.Logging": "5.4.0" } }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, "Polly": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" - }, - "RabbitMQ.Client": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1znR1gGU+xYVSpO5z8nQolcUKA/yydnxQn7Ug9+RUXxTSLMm/eE58VKGwahPBjELXvDnX0k/kBrAitFLRjx9LA==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "System.Memory": "4.5.4", - "System.Threading.Channels": "4.7.1" + "Polly.Core": "8.5.2" } }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" - }, - "runtime.native.System": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" - }, "SharpCompress": { "type": "Transitive", "resolved": "0.30.1", @@ -923,98 +792,83 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Swashbuckle.AspNetCore": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "eUBr4TW0up6oKDA5Xwkul289uqSMgY0xGN4pnbOIBqCcN9VKGGaPvHX3vWaG/hvocfGDP+MGzMA0bBBKz2fkmQ==", + "resolved": "8.0.0", + "contentHash": "K9FzGTxmwfD+7sVf/FTq/TZFHBTXcROgdcg7gLFwKwgvXwaqTtjGVdam27j0kYfgZZyWlOKr+abmtyd2nAd5eA==", "dependencies": { "Microsoft.Extensions.ApiDescription.Server": "6.0.5", - "Swashbuckle.AspNetCore.Swagger": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerGen": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerUI": "6.4.0" + "Swashbuckle.AspNetCore.Swagger": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerGen": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerUI": "8.0.0" } }, "Swashbuckle.AspNetCore.Swagger": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "nl4SBgGM+cmthUcpwO/w1lUjevdDHAqRvfUoe4Xp/Uvuzt9mzGUwyFCqa3ODBAcZYBiFoKvrYwz0rabslJvSmQ==", + "resolved": "8.0.0", + "contentHash": "+8Y4pVTWbnzotIk6d6rcwsHGpCchPDqqrvYkyGlI3go+pFaKM+4eX30iCyI0hvr0RMtObJCFhK6aDtlQFbEF1g==", "dependencies": { - "Microsoft.OpenApi": "1.2.3" + "Microsoft.OpenApi": "1.6.23" } }, "Swashbuckle.AspNetCore.SwaggerGen": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "lXhcUBVqKrPFAQF7e/ZeDfb5PMgE8n5t6L5B6/BQSpiwxgHzmBcx8Msu42zLYFTvR5PIqE9Q9lZvSQAcwCxJjw==", + "resolved": "8.0.0", + "contentHash": "skCeIQ93yMcUm1PQby5qitFM6KLIlLMj4/i8JHy86x2OFzxTNaaas2kUg6rNV3JvucFvYCNyImg7NMtZHErSzQ==", "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.4.0" + "Swashbuckle.AspNetCore.Swagger": "8.0.0" } }, "Swashbuckle.AspNetCore.SwaggerUI": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1Hh3atb3pi8c+v7n4/3N80Jj8RvLOXgWxzix6w3OZhB7zBGRwsy7FWr4e3hwgPweSBpwfElqj4V4nkjYabH9nQ==" + "resolved": "8.0.0", + "contentHash": "IMqmgclFiZL2QIfopOmWYofZzckrl+SdMt1h4mKC0jc94F+uzt3IHA3YFC0CGlwBqTTSnxHqNUKomNTeAhZbYA==" }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections": { + "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", "System.Runtime": "4.3.0" } }, - "System.Collections.Concurrent": { + "System.Buffers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -1024,297 +878,202 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" } }, - "System.Diagnostics.EventLog": { + "System.Composition.AttributedModel": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Diagnostics.Tracing": { + "System.Composition.Convention": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.AttributedModel": "6.0.0" } }, - "System.Globalization": { + "System.Composition.Hosting": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.Runtime": "6.0.0" } }, - "System.Globalization.Calendars": { + "System.Composition.Runtime": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" }, - "System.Globalization.Extensions": { + "System.Composition.TypedParts": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, - "System.IO": { + "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "Microsoft.IdentityModel.JsonWebTokens": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.FileSystem.Primitives": { + "System.IO.Hashing": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, - "System.Linq": { + "System.Linq.Expressions": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", "dependencies": { "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Async": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, - "System.Net.Http": { + "System.Net.Sockets": { "type": "Transitive", - "resolved": "4.3.4", - "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + "System.Threading.Tasks": "4.3.0" } }, - "System.Net.Primitives": { + "System.ObjectModel": { "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "OHzPhSme78BbmLe9UBxHM69ZYjClS5URuhce6Ta4ikiLgaUGiG/X84fZpI6zy7CsUH5R9cYzI2tv9dWPqdTkUg==", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "System.Runtime": "4.3.1", - "System.Runtime.Handles": "4.3.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Reactive": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" - }, - "System.Reactive.Linq": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "IB4/qlV4T1WhZvM11RVoFUSZXPow9VWVeQ1uDkSKgz6bAO+gCf65H/vjrYlwyXmojSSxvfHndF9qdH43P/IuAw==", - "dependencies": { - "System.Reactive": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } + "resolved": "6.0.0", + "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" }, - "System.Reflection": { + "System.Reflection.Emit": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "System.Reflection.Primitives": { + "System.Reflection.Emit.ILGeneration": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.Extensions": { + "System.Reflection.Emit.Lightweight": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime.Handles": { + "System.Reflection.Extensions": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime.InteropServices": { + "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" + "System.Collections.Immutable": "6.0.0" } }, - "System.Runtime.Numerics": { + "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -1325,229 +1084,44 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, - "System.Threading": { + "System.Threading.Channels": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" }, - "System.Threading.Channels": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==" - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -1555,182 +1129,173 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "monai.deploy.informaticsgateway": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "DotNext.Threading": "4.7.4", - "HL7-dotnetcore": "2.29.0", - "Karambolo.Extensions.Logging.File": "3.3.1", - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Hosting": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "1.0.0", - "Monai.Deploy.Messaging.RabbitMQ": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.MinIO": "0.2.10", - "NLog": "5.0.5", - "NLog.Web.AspNetCore": "5.1.5", - "Polly": "7.2.3", - "Swashbuckle.AspNetCore": "6.4.0", - "fo-dicom": "5.0.3", - "fo-dicom.NLog": "5.0.3" + "DotNext.Threading": "[5.5.0, )", + "HL7-dotnetcore": "[2.39.1, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution": "[1.0.0, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Security": "[1.0.1, )", + "Monai.Deploy.Storage.MinIO": "[1.0.2, )", + "NLog.Web.AspNetCore": "[5.4.0, )", + "Swashbuckle.AspNetCore": "[8.0.0, )" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client": { "type": "Project", "dependencies": { - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.MongoDB": "1.0.0" + "AspNetCore.HealthChecks.MongoDb": "[8.1.0, )", + "Microsoft.EntityFrameworkCore.Tools": "[8.0.14, )", + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.MongoDB": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.database.mongodb": { "type": "Project", "dependencies": { - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "MongoDB.Driver": "2.18.0", - "MongoDB.Driver.Core": "2.18.0" + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.plugins.remoteappexecution": { + "type": "Project", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "NLog": "[5.4.0, )", + "Polly": "[8.5.2, )" } } } diff --git a/src/Client/packages.lock.json b/src/Client/packages.lock.json index 5d93fec3e..10dba21f0 100644 --- a/src/Client/packages.lock.json +++ b/src/Client/packages.lock.json @@ -1,76 +1,52 @@ { "version": 1, "dependencies": { - "net6.0": { - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "Microsoft.AspNet.WebApi.Client": { - "type": "Direct", - "requested": "[5.2.9, )", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } - }, - "Microsoft.Extensions.Http": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } - }, + "net8.0": { "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -79,35 +55,31 @@ }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.EntityFrameworkCore.Abstractions": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.Extensions.Configuration": { + "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -115,41 +87,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -166,266 +149,92 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.2", - "contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.Toolkit.HighPerformance": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Newtonsoft.Json.Bson": { + "Polly": { "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" + "Polly.Core": "8.5.2" } }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -433,60 +242,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -495,714 +250,82 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.Compression.ZipFile": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Extensions": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Xml.XDocument": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/Common/ExtensionMethods.cs b/src/Common/ExtensionMethods.cs index 997311217..664f9abeb 100644 --- a/src/Common/ExtensionMethods.cs +++ b/src/Common/ExtensionMethods.cs @@ -76,5 +76,15 @@ public static async Task Post(this ActionBlock actionBlock await Task.Delay(delay).ConfigureAwait(false); return actionBlock.Post(input); } + + /// + /// Checks if a given task is faulted or cancelled. + /// + /// The task object + /// True if canceled or faulted. False otherwise. + public static bool IsCanceledOrFaulted(this Task task) + { + return task.IsCanceled || task.IsFaulted; + } } } diff --git a/src/Common/Monai.Deploy.InformaticsGateway.Common.csproj b/src/Common/Monai.Deploy.InformaticsGateway.Common.csproj index 4a74ecd74..8c4c29562 100644 --- a/src/Common/Monai.Deploy.InformaticsGateway.Common.csproj +++ b/src/Common/Monai.Deploy.InformaticsGateway.Common.csproj @@ -1,4 +1,4 @@ - - - - Monai.Deploy.InformaticsGateway.Common - net6.0 + net8.0 Apache-2.0 true True ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - - - - All - - - + + - - - + + + diff --git a/src/Common/Test/ExtensionMethodsTest.cs b/src/Common/Test/ExtensionMethodsTest.cs index 9158fb2d5..1d211b2b5 100644 --- a/src/Common/Test/ExtensionMethodsTest.cs +++ b/src/Common/Test/ExtensionMethodsTest.cs @@ -90,7 +90,7 @@ public async Task GivenAnActionBlock_WhenPostWIithDelayIsCalled_ExpectADelayBefo }); stopwatch.Start(); - await actionBlock.Post(input, delay).ConfigureAwait(false); + await actionBlock.Post(input, delay).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); } } } diff --git a/src/Common/Test/Monai.Deploy.InformaticsGateway.Common.Test.csproj b/src/Common/Test/Monai.Deploy.InformaticsGateway.Common.Test.csproj index 9ee1c5396..f892060d3 100644 --- a/src/Common/Test/Monai.Deploy.InformaticsGateway.Common.Test.csproj +++ b/src/Common/Test/Monai.Deploy.InformaticsGateway.Common.Test.csproj @@ -1,5 +1,5 @@ - - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Common.Test Apache-2.0 false true - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + \ No newline at end of file diff --git a/src/Common/Test/packages.lock.json b/src/Common/Test/packages.lock.json index b58ecbb75..781bfae40 100644 --- a/src/Common/Test/packages.lock.json +++ b/src/Common/Test/packages.lock.json @@ -1,1117 +1,138 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" + } + }, + "System.IO.Abstractions": { + "type": "Direct", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, "System.IO.Abstractions.TestingHelpers": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "tkXvQbsfOIfeoGso+WptCuouFLiWt3EU8s0D8poqIVz1BJOOszkPuFbFgP2HUTJ9bp5n1HH89eFHILo6Oz5XUw==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", "dependencies": { - "System.IO.Abstractions": "17.2.3" + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, - "fo-dicom": { - "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", - "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Channels": "6.0.0" - } - }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" - }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Primitives": "2.2.0", - "System.ComponentModel.Annotations": "4.5.0" - } - }, - "Microsoft.Extensions.Primitives": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", - "dependencies": { - "System.Memory": "4.5.1", - "System.Runtime.CompilerServices.Unsafe": "4.5.1" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Abstractions": { - "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "sDJYJpGtTgx+23Ayu5euxG5mAXWdkDb4+b0rD0Cab0M1oQS9H0HXGPriKcqpXuiJDTV7fTp/d+fMDJmnr6sNvA==" - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Runtime": { + "TestableIO.System.IO.Abstractions.TestingHelpers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" - }, - "System.Runtime.Extensions": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1121,51 +142,44 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/Common/packages.lock.json b/src/Common/packages.lock.json index 9a2b36402..a668f746e 100644 --- a/src/Common/packages.lock.json +++ b/src/Common/packages.lock.json @@ -1,145 +1,41 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Ardalis.GuardClauses": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } - }, - "fo-dicom": { - "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", - "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Channels": "6.0.0" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, - "GitVersion.MsBuild": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "System.IO.Abstractions": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.Threading.Tasks.Dataflow": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "JetBrains.Annotations": { - "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" - }, - "Microsoft.Extensions.DependencyInjection": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Primitives": "2.2.0", - "System.ComponentModel.Annotations": "4.5.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "Microsoft.Extensions.Primitives": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==", - "dependencies": { - "System.Memory": "4.5.1", - "System.Runtime.CompilerServices.Unsafe": "4.5.1" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Buffers": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg==" - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "sDJYJpGtTgx+23Ayu5euxG5mAXWdkDb4+b0rD0Cab0M1oQS9H0HXGPriKcqpXuiJDTV7fTp/d+fMDJmnr6sNvA==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw==" - }, - "System.Text.Encoding.CodePages": { - "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" } } } diff --git a/src/Configuration/ConfigurationException.cs b/src/Configuration/ConfigurationException.cs index a40a9a9d8..673dca6f1 100644 --- a/src/Configuration/ConfigurationException.cs +++ b/src/Configuration/ConfigurationException.cs @@ -16,14 +16,13 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Configuration { /// - /// Represnets an exception based upon invalid configuration. + /// Represents an exception based upon invalid configuration. /// - [Serializable] + public class ConfigurationException : Exception { public ConfigurationException() @@ -37,9 +36,5 @@ public ConfigurationException(string message) : base(message) public ConfigurationException(string message, Exception innerException) : base(message, innerException) { } - - protected ConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/Configuration/ConfigurationValidator.cs b/src/Configuration/ConfigurationValidator.cs old mode 100644 new mode 100755 index c19d175d4..ed8259e0c --- a/src/Configuration/ConfigurationValidator.cs +++ b/src/Configuration/ConfigurationValidator.cs @@ -120,7 +120,7 @@ private bool IsValidDirectory(string source, string directory) private bool IsValidBucketName(string source, string bucketName) { var valid = IsNotNullOrWhiteSpace(source, bucketName); - var regex = new Regex("(?=^.{3,63}$)(^[a-z0-9]+[a-z0-9\\-]+[a-z0-9]+$)"); + var regex = new Regex("(?=^.{3,63}$)(^[a-z0-9]+[a-z0-9\\-]+[a-z0-9]+$)", RegexOptions.None, TimeSpan.FromSeconds(5)); if (!regex.IsMatch(bucketName)) { valid = false; diff --git a/src/Configuration/DatabaseConfiguration.cs b/src/Configuration/DatabaseOptions.cs old mode 100644 new mode 100755 similarity index 83% rename from src/Configuration/DatabaseConfiguration.cs rename to src/Configuration/DatabaseOptions.cs index 342bdfafb..dcd573850 --- a/src/Configuration/DatabaseConfiguration.cs +++ b/src/Configuration/DatabaseOptions.cs @@ -1,5 +1,5 @@ -/* - * Copyright 2021-2022 MONAI Consortium +/* + * Copyright 2022 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ namespace Monai.Deploy.InformaticsGateway.Configuration { - public class DatabaseConfiguration + public class DatabaseOptions { + [ConfigurationKeyName("DatabaseName")] + public string DatabaseName { get; set; } = string.Empty; + /// /// Gets or sets retry options relate to reading/writing to the database. /// diff --git a/src/Configuration/DicomWebConfiguration.cs b/src/Configuration/DicomWebConfiguration.cs index f57a58ab7..8fde9b334 100644 --- a/src/Configuration/DicomWebConfiguration.cs +++ b/src/Configuration/DicomWebConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ +using System.Collections.Generic; using Microsoft.Extensions.Configuration; namespace Monai.Deploy.InformaticsGateway.Configuration @@ -30,8 +31,9 @@ public class DicomWebConfiguration /// /// Gets or sets the (postfix) name of the DICOMweb export agent used for receiving messages. - /// The agent name is combine with - /// for subscribing messages from the message broker service. + /// This value is appended to + /// as the name for subscribing to messages from the message broker service. + /// [ConfigurationKeyName("agentName")] public string AgentName { get; set; } = "monaidicomweb"; @@ -39,7 +41,7 @@ public class DicomWebConfiguration /// Gets or sets the maximum number of simultaneous DICOMweb connections. /// [ConfigurationKeyName("maximumNumberOfConnections")] - public int MaximumNumberOfConnection { get; set; } = 2; + public ushort MaximumNumberOfConnection { get; set; } = 2; /// /// Gets or set the maximum allowed file size in bytes with default to 2GiB. @@ -55,10 +57,23 @@ public class DicomWebConfiguration /// single POST request, therefore, the timeout value may be insignificant unless the load of the /// network affects the upload speed. /// - public uint Timeout { get; set; } = 2; + [ConfigurationKeyName("timeout")] + public uint Timeout { get; set; } = 10; + + /// + /// Optional list of data input plug-in type names to be executed by the *IInputDataPlugInEngine* + /// on the data received using default DICOMWeb STOW-RS endpoints: + /// + /// POST /dicomweb/studies/[{study-instance-uid}] + /// POST /dicomweb/{workflow-id}/studies/[{study-instance-uid}] + /// + /// + [ConfigurationKeyName("plugins")] + public List PlugInAssemblies { get; set; } = default!; public DicomWebConfiguration() { + PlugInAssemblies ??= new List(); } } } diff --git a/src/Configuration/HttpEndpointSettings.cs b/src/Configuration/HttpEndpointSettings.cs new file mode 100644 index 000000000..d62a045a6 --- /dev/null +++ b/src/Configuration/HttpEndpointSettings.cs @@ -0,0 +1,30 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Configuration; + +namespace Monai.Deploy.InformaticsGateway.Configuration +{ + public class HttpEndpointSettings + { + [ConfigurationKeyName("defaultPageSize")] + public int DefaultPageSize { get; set; } = 10; + + [ConfigurationKeyName("maxPageSize")] + public int MaxPageSize { get; set; } = 10; + } +} diff --git a/src/Configuration/InformaticsGatewayConfiguration.cs b/src/Configuration/InformaticsGatewayConfiguration.cs old mode 100644 new mode 100755 index f2a466087..7b0bd11fd --- a/src/Configuration/InformaticsGatewayConfiguration.cs +++ b/src/Configuration/InformaticsGatewayConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -70,12 +70,6 @@ public class InformaticsGatewayConfiguration [ConfigurationKeyName("messaging")] public MessageBrokerConfiguration Messaging { get; set; } - /// - /// Represents the database section of the configuration file. - /// - [ConfigurationKeyName("database")] - public DatabaseConfiguration Database { get; set; } - public InformaticsGatewayConfiguration() { Dicom = new DicomConfiguration(); @@ -84,7 +78,6 @@ public InformaticsGatewayConfiguration() Fhir = new FhirConfiguration(); Export = new DataExportConfiguration(); Messaging = new MessageBrokerConfiguration(); - Database = new DatabaseConfiguration(); Hl7 = new Hl7Configuration(); } } diff --git a/src/Configuration/MessageBrokerConfigurationKeys.cs b/src/Configuration/MessageBrokerConfigurationKeys.cs old mode 100644 new mode 100755 index 897e25eb6..54c4da5a3 --- a/src/Configuration/MessageBrokerConfigurationKeys.cs +++ b/src/Configuration/MessageBrokerConfigurationKeys.cs @@ -28,17 +28,46 @@ public class MessageBrokerConfigurationKeys public string WorkflowRequest { get; set; } = "md.workflow.request"; /// - /// Gets or sets the topic for publishing workflow requests. - /// Defaults to `md_workflow_request`. + /// Gets or sets the topic for publishing export complete requests. + /// Defaults to `md_export_complete`. /// [ConfigurationKeyName("exportComplete")] public string ExportComplete { get; set; } = "md.export.complete"; /// - /// Gets or sets the topic for publishing workflow requests. - /// Defaults to `md_workflow_request`. + /// Gets or sets the topic for publishing export requests. + /// Defaults to `md_export_request`. /// [ConfigurationKeyName("exportRequestPrefix")] public string ExportRequestPrefix { get; set; } = "md.export.request"; + + /// + /// Gets or sets the topic for publishing artifact recieved events. + /// Defaults to `md_workflow_artifactrecieved`. + /// + [ConfigurationKeyName("artifactrecieved")] + public string ArtifactRecieved { get; set; } = "md.workflow.artifactrecieved"; + + + /// + /// Gets or sets the topic for publishing export requests. + /// Defaults to `md_export_request`. + /// + [ConfigurationKeyName("externalAppRequest")] + public string ExternalAppRequest { get; set; } = "md.externalapp.request"; + + /// + /// Gets or sets the topic for publishing workflow requests. + /// Defaults to `md.export.request`. + /// + [ConfigurationKeyName("exportHl7")] + public string ExportHL7 { get; set; } = "md.export.hl7"; + + /// + /// Gets or sets the topic for publishing export complete requests. + /// Defaults to `md_export_complete`. + /// + [ConfigurationKeyName("exportHl7Complete")] + public string ExportHl7Complete { get; set; } = "md.export.hl7complete"; } } diff --git a/src/Configuration/Monai.Deploy.InformaticsGateway.Configuration.csproj b/src/Configuration/Monai.Deploy.InformaticsGateway.Configuration.csproj index 8b9dacb3c..6c56a34e6 100644 --- a/src/Configuration/Monai.Deploy.InformaticsGateway.Configuration.csproj +++ b/src/Configuration/Monai.Deploy.InformaticsGateway.Configuration.csproj @@ -1,4 +1,4 @@ - - - Monai.Deploy.InformaticsGateway.Configuration - net6.0 + net8.0 Apache-2.0 true ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - - - All - - - - - - - - - - + + + + + + - + \ No newline at end of file diff --git a/src/Configuration/PluginConfiguration.cs b/src/Configuration/PluginConfiguration.cs new file mode 100755 index 000000000..9a4f8fc0b --- /dev/null +++ b/src/Configuration/PluginConfiguration.cs @@ -0,0 +1,36 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; +using Microsoft.Extensions.Configuration; + +<<<<<<<< HEAD:src/Configuration/PluginConfiguration.cs +namespace Monai.Deploy.InformaticsGateway.Configuration +{ + public class PlugInConfiguration + { + [ConfigurationKeyName("remoteApp")] + public Dictionary RemoteAppConfigurations { get; set; } = new(); +======== +namespace Monai.Deploy.InformaticsGateway.Database.Api +{ + public class DatabaseOptions + { + [ConfigurationKeyName("DatabaseName")] + public string DatabaseName { get; set; } = string.Empty; +>>>>>>>> 0ba5ba56 (Release/0.4.0 (#458)):src/Database/Api/DatabaseOptions.cs + } +} diff --git a/src/Configuration/RetryConfiguration.cs b/src/Configuration/RetryConfiguration.cs old mode 100644 new mode 100755 index f59f4cf5d..ae9b343fc --- a/src/Configuration/RetryConfiguration.cs +++ b/src/Configuration/RetryConfiguration.cs @@ -29,7 +29,7 @@ public class RetryConfiguration /// Default is 250, 500, 1000. /// [ConfigurationKeyName("delays")] - public int[] DelaysMilliseconds { get; set; } = new[] { 250, 500, 1000 }; + public int[] DelaysMilliseconds { get; set; } // Gets the delays in TimeSpan objects public IEnumerable RetryDelays @@ -42,5 +42,12 @@ public IEnumerable RetryDelays } } } + public RetryConfiguration() + { + if (DelaysMilliseconds is null || DelaysMilliseconds.Length == 0) + { + DelaysMilliseconds = new[] { 250, 500, 1000 }; + } + } } } diff --git a/src/Configuration/ScpConfiguration.cs b/src/Configuration/ScpConfiguration.cs old mode 100644 new mode 100755 index 6b66626ee..bac9a2952 --- a/src/Configuration/ScpConfiguration.cs +++ b/src/Configuration/ScpConfiguration.cs @@ -34,6 +34,12 @@ public class ScpConfiguration [ConfigurationKeyName("port")] public int Port { get; set; } = 104; + /// + /// Gets or sets Port number to be used for SCP service. + /// + [ConfigurationKeyName("externalAppPort")] + public int ExternalAppPort { get; set; } = 105; + /// /// Gets or sets maximum number of simultaneous DICOM associations for the SCP service. /// diff --git a/src/Configuration/ScuConfiguration.cs b/src/Configuration/ScuConfiguration.cs index 39a9e5c94..5f0544f52 100644 --- a/src/Configuration/ScuConfiguration.cs +++ b/src/Configuration/ScuConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,8 +32,8 @@ public class ScuConfiguration /// /// Gets or sets the (postfix) name of the DIMSE export agent used for receiving messages. - /// The agent name is combine with - /// for subscribing messages from the message broker service. + /// This value is appended to + /// as the name for subscribing to messages from the message broker service. /// [ConfigurationKeyName("agentName")] public string AgentName { get; set; } = "monaiscu"; @@ -54,7 +54,7 @@ public class ScuConfiguration /// Gets or sets the maximum number of simultaneous DICOM associations for the SCU service. /// [ConfigurationKeyName("maximumNumberOfAssociations")] - public int MaximumNumberOfAssociations { get; set; } = 8; + public ushort MaximumNumberOfAssociations { get; set; } = 8; public ScuConfiguration() { diff --git a/src/Configuration/StorageConfiguration.cs b/src/Configuration/StorageConfiguration.cs index ab7d10141..6a8575ad2 100644 --- a/src/Configuration/StorageConfiguration.cs +++ b/src/Configuration/StorageConfiguration.cs @@ -43,7 +43,6 @@ public class StorageConfiguration : StorageServiceConfiguration [ConfigurationKeyName("bufferSize")] public int BufferSize { get; set; } = 128000; - /// /// Gets or set the maximum memory buffer size in bytes with default to 30MiB. /// @@ -104,5 +103,12 @@ public class StorageConfiguration : StorageServiceConfiguration /// [ConfigurationKeyName("concurrentUploads")] public int ConcurrentUploads { get; set; } = 2; + + /// + /// Gets or set the timeout value, in milliseconds, for calls made to the storage service. Default is 5000; + /// This applies to the following calls: ListObjectsAsync, VerifyObjectsExistAsync, VerifyObjectExistsAsync, ListObjectsWithCredentialsAsync. + /// + [ConfigurationKeyName("storageServiceListTimeout")] + public int StorageServiceListTimeout { get; set; } = 5000; } } diff --git a/src/Configuration/Test/ConfigurationValidatorTest.cs b/src/Configuration/Test/ConfigurationValidatorTest.cs index 93c39996e..58f681164 100644 --- a/src/Configuration/Test/ConfigurationValidatorTest.cs +++ b/src/Configuration/Test/ConfigurationValidatorTest.cs @@ -17,6 +17,7 @@ using System; using System.IO; using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.SharedTest; @@ -28,21 +29,19 @@ namespace Monai.Deploy.InformaticsGateway.Configuration.Test public class ConfigurationValidatorTest { private readonly Mock> _logger; - private readonly Mock _fileSystem; + private readonly IFileSystem _fileSystem; public ConfigurationValidatorTest() { _logger = new Mock>(); - _fileSystem = new Mock(); - _fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); - _fileSystem.Setup(p => p.File.Create(It.IsAny(), It.IsAny(), It.IsAny())).Returns(FileStream.Null); + _fileSystem = new MockFileSystem(); } [Fact(DisplayName = "ConfigurationValidator test with all valid settings")] public void AllValid() { var config = MockValidConfiguration(); - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); Assert.True(valid == ValidateOptionsResult.Success); } @@ -52,7 +51,7 @@ public void InvalidScpPort() var config = MockValidConfiguration(); config.Dicom.Scp.Port = Int32.MaxValue; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessage = $"Invalid port number '{Int32.MaxValue}' specified for InformaticsGateway>dicom>scp>port."; Assert.Equal(validationMessage, valid.FailureMessage); @@ -65,7 +64,7 @@ public void InvalidScpMaxAssociations() var config = MockValidConfiguration(); config.Dicom.Scp.MaximumNumberOfAssociations = 0; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessage = $"Value of InformaticsGateway>dicom>scp>max-associations must be between {1} and {1000}."; Assert.Equal(validationMessage, valid.FailureMessage); @@ -78,7 +77,7 @@ public void StorageWithInvalidWatermark() var config = MockValidConfiguration(); config.Storage.Watermark = 1000; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessage = "Value of InformaticsGateway>storage>watermark must be between 1 and 100."; Assert.Equal(validationMessage, valid.FailureMessage); @@ -91,7 +90,7 @@ public void StorageWithInvalidReservedSpace() var config = MockValidConfiguration(); config.Storage.ReserveSpaceGB = 9999; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessage = "Value of InformaticsGateway>storage>reserveSpaceGB must be between 1 and 999."; Assert.Equal(validationMessage, valid.FailureMessage); @@ -104,7 +103,7 @@ public void StorageWithInvalidTemporaryBucketName() var config = MockValidConfiguration(); config.Storage.TemporaryStorageBucket = " "; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessages = new[] { "Value for InformaticsGateway>storage>temporaryBucketName is required.", "Value for InformaticsGateway>storage>temporaryBucketName does not conform to Amazon S3 bucket naming requirements." }; Assert.Equal(string.Join(Environment.NewLine, validationMessages), valid.FailureMessage); @@ -120,7 +119,7 @@ public void StorageWithInvalidBucketName() var config = MockValidConfiguration(); config.Storage.StorageServiceBucketName = ""; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, _fileSystem).Validate("", config); var validationMessages = new[] { "Value for InformaticsGateway>storage>bucketName is required.", "Value for InformaticsGateway>storage>bucketName does not conform to Amazon S3 bucket naming requirements." }; Assert.Equal(string.Join(Environment.NewLine, validationMessages), valid.FailureMessage); @@ -133,13 +132,15 @@ public void StorageWithInvalidBucketName() [Fact(DisplayName = "ConfigurationValidator test with inaccessible directory")] public void StorageWithInaccessbleDirectory() { - _fileSystem.Setup(p => p.File.Create(It.IsAny(), It.IsAny(), It.IsAny())).Throws(new UnauthorizedAccessException("error")); + var fileSystem = new Mock(); + fileSystem.Setup(p => p.Directory.Exists(It.IsAny())).Returns(true); + fileSystem.Setup(p => p.File.Create(It.IsAny(), It.IsAny(), It.IsAny())).Throws(new UnauthorizedAccessException("error")); var config = MockValidConfiguration(); config.Storage.TemporaryDataStorage = TemporaryDataStorageLocation.Disk; config.Storage.LocalTemporaryStoragePath = "/blabla"; - var valid = new ConfigurationValidator(_logger.Object, _fileSystem.Object).Validate("", config); + var valid = new ConfigurationValidator(_logger.Object, fileSystem.Object).Validate("", config); var validationMessages = new[] { $"Directory `/blabla` specified in `InformaticsGateway>storage>localTemporaryStoragePath` is not accessible: error." }; Assert.Equal(string.Join(Environment.NewLine, validationMessages), valid.FailureMessage); diff --git a/src/Configuration/Test/Monai.Deploy.InformaticsGateway.Configuration.Test.csproj b/src/Configuration/Test/Monai.Deploy.InformaticsGateway.Configuration.Test.csproj index 7b5aecc82..fe22b796c 100644 --- a/src/Configuration/Test/Monai.Deploy.InformaticsGateway.Configuration.Test.csproj +++ b/src/Configuration/Test/Monai.Deploy.InformaticsGateway.Configuration.Test.csproj @@ -1,5 +1,5 @@ - - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Configuration.Test Apache-2.0 false true - - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + \ No newline at end of file diff --git a/src/Configuration/Test/ValidationExtensionsTest.cs b/src/Configuration/Test/ValidationExtensionsTest.cs old mode 100644 new mode 100755 index ec452a886..9e00e5dbb --- a/src/Configuration/Test/ValidationExtensionsTest.cs +++ b/src/Configuration/Test/ValidationExtensionsTest.cs @@ -15,8 +15,10 @@ */ using System; +using System.Collections.Generic; using FellowOakDicom; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Xunit; namespace Monai.Deploy.InformaticsGateway.Configuration.Test @@ -202,5 +204,90 @@ public void SourceApplicationEntity_Valid() } #endregion SourceApplicationEntity.IsValid + + #region IsAeTitleValid + + [Theory] + [InlineData("123")] + [InlineData("MYAET")] + [InlineData("EAST-123-123")] + public void GivenAValidAETitle_WhenIIsAeTitleValid_ExpectToReturnTrue(string value) + { + var errors = new List(); + Assert.True(ValidationExtensions.IsAeTitleValid("test", value, errors)); + Assert.Empty(errors); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("AE\\")] + [InlineData("AE/")] + [InlineData("1$")] + [InlineData("A.E.T.")] + public void GivenAnInvalidAETitle_WhenIsAeTitleValid_ExpectToReturnFalse(string value) + { + var errors = new List(); + Assert.False(ValidationExtensions.IsAeTitleValid("test", value, errors)); + Assert.NotEmpty(errors); + } + + #endregion IsAeTitleValid + + #region IsValidHostNameIp + + [Theory] + [InlineData("0.0.0.0")] + [InlineData("10.20.30.40")] + [InlineData("255.255.255.0")] + [InlineData("1.2.3.4")] + [InlineData("192.168.0.1")] + public void GivenAValidIpAddress_WhenIsValidHostNameIpIsCalled_ExpectToReturnTrue(string value) + { + var errors = new List(); + Assert.True(ValidationExtensions.IsValidHostNameIp("test", value, errors)); + Assert.Empty(errors); + } + + [Theory] + [InlineData("256.256.256.256")] + [InlineData("1.0")] + [InlineData("1")] + [InlineData("2.3.4")] + public void GivenAnInvalidIpAddress_WhenIsValidHostNameIpIsCalled_ExpectToReturnFalse(string value) + { + var errors = new List(); + Assert.False(ValidationExtensions.IsValidHostNameIp("test", value, errors)); + Assert.NotEmpty(errors); + } + + [Theory] + [InlineData("localhost")] + [InlineData("east-1")] + [InlineData("east-2.k8s.local")] + [InlineData("cloud.com")] + [InlineData("east.cloud.com")] + [InlineData("super.west.cloud.com")] + [InlineData("example-mongodb-0.example-mongodb-svc.monai-deploy.svc.cluster.local")] + public void GivenAValidHostName_WhenIsValidHostNameIpIsCalled_ExpectToReturnTrue(string value) + { + var errors = new List(); + Assert.True(ValidationExtensions.IsValidHostNameIp("test", value, errors)); + Assert.Empty(errors); + } + + [Theory] + [InlineData("localhost!")] + [InlineData("cloud@com")] + [InlineData("east@cloud.com")] + [InlineData("super/west.cloud.com")] + public void GivenAnInvalidHostName_WhenIsValidHostNameIpIsCalled_ExpectToReturnFalse(string value) + { + var errors = new List(); + Assert.False(ValidationExtensions.IsValidHostNameIp("test", value, errors)); + Assert.NotEmpty(errors); + } + + #endregion IsValidHostNameIp } } diff --git a/src/Configuration/Test/packages.lock.json b/src/Configuration/Test/packages.lock.json index c6a202ed0..65936ecde 100644 --- a/src/Configuration/Test/packages.lock.json +++ b/src/Configuration/Test/packages.lock.json @@ -1,107 +1,111 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "System.IO.Abstractions.TestingHelpers": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "tkXvQbsfOIfeoGso+WptCuouFLiWt3EU8s0D8poqIVz1BJOOszkPuFbFgP2HUTJ9bp5n1HH89eFHILo6Oz5XUw==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", "dependencies": { - "System.IO.Abstractions": "17.2.3" + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -109,41 +113,37 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -151,41 +151,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -202,280 +213,109 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "runtime.native.System.Net.Http": { + "Polly": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "Polly.Core": "8.5.2" } }, - "runtime.native.System.Security.Cryptography.Apple": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -483,60 +323,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -550,686 +336,70 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Linq": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions.TestingHelpers": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Threading.Timer": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1239,73 +409,64 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } } } diff --git a/src/Configuration/ValidationExtensions.cs b/src/Configuration/ValidationExtensions.cs old mode 100644 new mode 100755 index 9f064716e..a326b6283 --- a/src/Configuration/ValidationExtensions.cs +++ b/src/Configuration/ValidationExtensions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,11 +15,14 @@ * limitations under the License. */ +using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using Ardalis.GuardClauses; using FellowOakDicom; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Configuration { @@ -29,7 +32,7 @@ public static class ValidationExtensions public static bool IsValid(this MonaiApplicationEntity monaiApplicationEntity, out IList validationErrors) { - Guard.Against.Null(monaiApplicationEntity); + Guard.Against.Null(monaiApplicationEntity, nameof(monaiApplicationEntity)); validationErrors = new List(); @@ -42,7 +45,7 @@ public static bool IsValid(this MonaiApplicationEntity monaiApplicationEntity, o public static bool IsValid(this DestinationApplicationEntity destinationApplicationEntity, out IList validationErrors) { - Guard.Against.Null(destinationApplicationEntity); + Guard.Against.Null(destinationApplicationEntity, nameof(destinationApplicationEntity)); validationErrors = new List(); @@ -55,9 +58,23 @@ public static bool IsValid(this DestinationApplicationEntity destinationApplicat return valid; } + public static bool IsValid(this HL7DestinationEntity hl7destinationEntity, out IList validationErrors) + { + Guard.Against.Null(hl7destinationEntity, nameof(hl7destinationEntity)); + + validationErrors = new List(); + + var valid = true; + valid &= !string.IsNullOrWhiteSpace(hl7destinationEntity.Name); + valid &= IsValidHostNameIp(hl7destinationEntity.Name, hl7destinationEntity.HostIp, validationErrors); + valid &= IsPortValid(hl7destinationEntity.GetType().Name, hl7destinationEntity.Port, validationErrors); + + return valid; + } + public static bool IsValid(this SourceApplicationEntity sourceApplicationEntity, out IList validationErrors) { - Guard.Against.Null(sourceApplicationEntity); + Guard.Against.Null(sourceApplicationEntity, nameof(sourceApplicationEntity)); validationErrors = new List(); @@ -68,9 +85,23 @@ public static bool IsValid(this SourceApplicationEntity sourceApplicationEntity, return valid; } + public static bool IsValid(this VirtualApplicationEntity virtualApplicationEntity, out IList validationErrors) + { + Guard.Against.Null(virtualApplicationEntity, nameof(virtualApplicationEntity)); + + validationErrors = new List(); + + var valid = true; + + // The virtual AE Title is used as a URL fragment but given that AE Title has stricter character sets, we will use the same validation. + valid &= IsAeTitleValid("virtualAeTitle", virtualApplicationEntity.VirtualAeTitle, validationErrors); + + return valid; + } + public static bool IsValidDicomTag(string source, string grouping, IList validationErrors = null) { - Guard.Against.NullOrWhiteSpace(source); + Guard.Against.NullOrWhiteSpace(source, nameof(source)); try { @@ -92,9 +123,14 @@ public static bool IsValidDicomTag(string source, string grouping, IList public static bool IsAeTitleValid(string source, string aeTitle, IList validationErrors = null) { - Guard.Against.NullOrWhiteSpace(source); + Guard.Against.NullOrWhiteSpace(source, nameof(source)); - if (!string.IsNullOrWhiteSpace(aeTitle) && aeTitle.Length <= 15) return true; + if (!string.IsNullOrWhiteSpace(aeTitle) && + aeTitle.Length <= 15 && + Regex.IsMatch(aeTitle, @"^[a-zA-Z0-9_\-]+$", RegexOptions.None, TimeSpan.FromSeconds(2))) + { + return true; + } validationErrors?.Add($"'{aeTitle}' is not a valid AE Title (source: {source})."); return false; @@ -102,7 +138,12 @@ public static bool IsAeTitleValid(string source, string aeTitle, IList v public static bool IsValidHostNameIp(string source, string hostIp, IList validationErrors = null) { - if (!string.IsNullOrWhiteSpace(hostIp)) return true; + if (!string.IsNullOrWhiteSpace(hostIp) && + (Regex.IsMatch(hostIp, @"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$", RegexOptions.None, TimeSpan.FromSeconds(2)) || // IP address + Regex.IsMatch(hostIp, @"^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$", RegexOptions.None, TimeSpan.FromSeconds(2)))) // Host/domain name + { + return true; + } validationErrors?.Add($"Invalid host name/IP address '{hostIp}' specified for {source}."); return false; @@ -110,7 +151,7 @@ public static bool IsValidHostNameIp(string source, string hostIp, IList public static bool IsPortValid(string source, int port, IList validationErrors = null) { - Guard.Against.NullOrWhiteSpace(source); + Guard.Against.NullOrWhiteSpace(source, nameof(source)); if (port > 0 && port <= 65535) return true; diff --git a/src/Configuration/packages.lock.json b/src/Configuration/packages.lock.json index 45db310bc..5a8efa487 100644 --- a/src/Configuration/packages.lock.json +++ b/src/Configuration/packages.lock.json @@ -1,106 +1,58 @@ { "version": 1, "dependencies": { - "net6.0": { - "GitVersion.MsBuild": { + "net8.0": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Direct", - "requested": "[6.0.3, )", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Monai.Deploy.Messaging": { - "type": "Direct", - "requested": "[0.1.16, )", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" - } - }, - "Monai.Deploy.Storage": { - "type": "Direct", - "requested": "[0.2.10, )", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", - "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" - } - }, - "System.IO.Abstractions": { - "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -108,36 +60,32 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -145,41 +93,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -194,48 +153,101 @@ "System.Diagnostics.DiagnosticSource": "6.0.0" } }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Monai.Deploy.Messaging": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Microsoft.NETCore.Platforms": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" + } }, - "Microsoft.Toolkit.HighPerformance": { + "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" + } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "Polly": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -244,6 +256,20 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -251,49 +277,55 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } } } diff --git a/src/Database/Api/DatabaseException.cs b/src/Database/Api/DatabaseException.cs index 9191a0768..fd61379ba 100644 --- a/src/Database/Api/DatabaseException.cs +++ b/src/Database/Api/DatabaseException.cs @@ -14,11 +14,8 @@ * limitations under the License. */ -using System.Runtime.Serialization; - namespace Monai.Deploy.InformaticsGateway.Database.Api { - [Serializable] public class DatabaseException : Exception { public DatabaseException() @@ -32,9 +29,5 @@ public DatabaseException(string? message) : base(message) public DatabaseException(string? message, Exception? innerException) : base(message, innerException) { } - - protected DatabaseException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/Database/Api/DatabaseRegistrationBase.cs b/src/Database/Api/DatabaseRegistrationBase.cs new file mode 100755 index 000000000..0c40bed90 --- /dev/null +++ b/src/Database/Api/DatabaseRegistrationBase.cs @@ -0,0 +1,27 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Monai.Deploy.InformaticsGateway.Database.Api +{ + public abstract class DatabaseRegistrationBase + { + public abstract IServiceCollection Configure(IServiceCollection services, DatabaseType databaseType, IConfigurationSection? connectionstringConfigurationSection, IConfigurationSection? pluginsConfigurationSection, ILoggerFactory loggerFactory); + } +} diff --git a/src/Database/Api/DatabaseTypes.cs b/src/Database/Api/DatabaseTypes.cs new file mode 100644 index 000000000..166c8f5d1 --- /dev/null +++ b/src/Database/Api/DatabaseTypes.cs @@ -0,0 +1,24 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Monai.Deploy.InformaticsGateway.Database.Api +{ + public enum DatabaseType + { + EntityFramework, + MongoDb + } +} diff --git a/src/Database/Api/IDatabaseMigrationManager.cs b/src/Database/Api/IDatabaseMigrationManager.cs index 3de2f9abf..cf2fc1861 100644 --- a/src/Database/Api/IDatabaseMigrationManager.cs +++ b/src/Database/Api/IDatabaseMigrationManager.cs @@ -18,8 +18,19 @@ namespace Monai.Deploy.InformaticsGateway.Database.Api { + /// + /// Interface for the main application to migrate and configure databases + /// public interface IDatabaseMigrationManager { IHost Migrate(IHost host); } + + /// + /// Interface for the plug-ins to migrate and configure databases + /// + public interface IDatabaseMigrationManagerForPlugIns + { + IHost Migrate(IHost host); + } } diff --git a/src/Database/Api/Monai.Deploy.InformaticsGateway.Database.Api.csproj b/src/Database/Api/Monai.Deploy.InformaticsGateway.Database.Api.csproj index 00dd3bc3a..41cb9462e 100644 --- a/src/Database/Api/Monai.Deploy.InformaticsGateway.Database.Api.csproj +++ b/src/Database/Api/Monai.Deploy.InformaticsGateway.Database.Api.csproj @@ -13,31 +13,28 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - Monai.Deploy.InformaticsGateway.Database.Api - net6.0 + net8.0 enable enable true + false - - - - + + + + - - - + \ No newline at end of file diff --git a/src/Database/Api/Repositories/IDestinationApplicationEntityRepository.cs b/src/Database/Api/Repositories/IDestinationApplicationEntityRepository.cs old mode 100644 new mode 100755 index ffc58fa17..dd84793fa --- a/src/Database/Api/Repositories/IDestinationApplicationEntityRepository.cs +++ b/src/Database/Api/Repositories/IDestinationApplicationEntityRepository.cs @@ -15,7 +15,7 @@ */ using System.Linq.Expressions; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories { diff --git a/src/Database/Api/Repositories/IDicomAssociationInfoRepository.cs b/src/Database/Api/Repositories/IDicomAssociationInfoRepository.cs new file mode 100755 index 000000000..796ff43bc --- /dev/null +++ b/src/Database/Api/Repositories/IDicomAssociationInfoRepository.cs @@ -0,0 +1,42 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories +{ + public interface IDicomAssociationInfoRepository + { + Task> ToListAsync(CancellationToken cancellationToken = default); + + Task AddAsync(DicomAssociationInfo item, CancellationToken cancellationToken = default); + + /// + /// Retrieves a list of DicomAssociationInfo in the database. + /// + Task> GetAllAsync(int skip, + int? limit, + DateTime startTime, + DateTime endTime, + CancellationToken cancellationToken); + + /// + /// Gets count of objects + /// + /// Count of objects. + Task CountAsync(); + } +} diff --git a/src/Database/Api/Repositories/IExternalAppDeatilsRepository.cs b/src/Database/Api/Repositories/IExternalAppDeatilsRepository.cs new file mode 100755 index 000000000..51668aefd --- /dev/null +++ b/src/Database/Api/Repositories/IExternalAppDeatilsRepository.cs @@ -0,0 +1,31 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories +{ + public interface IExternalAppDetailsRepository + { + Task AddAsync(ExternalAppDetails details, CancellationToken cancellationToken); + + Task> GetAsync(string studyInstanceId, CancellationToken cancellationToken); + + Task GetByPatientIdOutboundAsync(string patientId, CancellationToken cancellationToken); + + Task GetByStudyIdOutboundAsync(string studyInstanceId, CancellationToken cancellationToken); + } +} diff --git a/src/Database/Api/Repositories/IHL7DestinationEntityRepository.cs b/src/Database/Api/Repositories/IHL7DestinationEntityRepository.cs new file mode 100644 index 000000000..c46dc3503 --- /dev/null +++ b/src/Database/Api/Repositories/IHL7DestinationEntityRepository.cs @@ -0,0 +1,36 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories +{ + public interface IHL7DestinationEntityRepository + { + Task> ToListAsync(CancellationToken cancellationToken = default); + + Task FindByNameAsync(string name, CancellationToken cancellationToken = default); + + Task AddAsync(HL7DestinationEntity item, CancellationToken cancellationToken = default); + + Task UpdateAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default); + + Task RemoveAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default); + + Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default); + } +} diff --git a/src/Database/Api/Repositories/IHl7ApplicationConfigRepository.cs b/src/Database/Api/Repositories/IHl7ApplicationConfigRepository.cs new file mode 100644 index 000000000..b381d4da6 --- /dev/null +++ b/src/Database/Api/Repositories/IHl7ApplicationConfigRepository.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api; + +namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories +{ + public interface IHl7ApplicationConfigRepository + { + Task> GetAllAsync(CancellationToken cancellationToken = default); + + Task GetByIdAsync(string id); + + Task DeleteAsync(string id, CancellationToken cancellationToken = default); + + Task CreateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default); + + Task UpdateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default); + } +} diff --git a/src/Database/Api/Repositories/IMonaiApplicationEntityRepository.cs b/src/Database/Api/Repositories/IMonaiApplicationEntityRepository.cs old mode 100644 new mode 100755 index 1b803385e..ccfbd6567 --- a/src/Database/Api/Repositories/IMonaiApplicationEntityRepository.cs +++ b/src/Database/Api/Repositories/IMonaiApplicationEntityRepository.cs @@ -15,7 +15,7 @@ */ using System.Linq.Expressions; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories { diff --git a/src/Database/Api/Repositories/IPayloadRepository.cs b/src/Database/Api/Repositories/IPayloadRepository.cs index 5449af8da..6c6e28d1f 100644 --- a/src/Database/Api/Repositories/IPayloadRepository.cs +++ b/src/Database/Api/Repositories/IPayloadRepository.cs @@ -14,7 +14,6 @@ * limitations under the License. */ -using System.Linq.Expressions; using Monai.Deploy.InformaticsGateway.Api.Storage; namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories @@ -29,8 +28,6 @@ public interface IPayloadRepository Task RemoveAsync(Payload entity, CancellationToken cancellationToken = default); - Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default); - Task RemovePendingPayloadsAsync(CancellationToken cancellationToken = default); Task> GetPayloadsInStateAsync(CancellationToken cancellationToken = default, params Payload.PayloadState[] states); diff --git a/src/Database/Api/Repositories/ISourceApplicationEntityRepository.cs b/src/Database/Api/Repositories/ISourceApplicationEntityRepository.cs old mode 100644 new mode 100755 index 6826ca639..9d73778a6 --- a/src/Database/Api/Repositories/ISourceApplicationEntityRepository.cs +++ b/src/Database/Api/Repositories/ISourceApplicationEntityRepository.cs @@ -25,6 +25,8 @@ public interface ISourceApplicationEntityRepository Task FindByNameAsync(string name, CancellationToken cancellationToken = default); + Task FindByAETAsync(string aeTitle, CancellationToken cancellationToken = default); + Task AddAsync(SourceApplicationEntity item, CancellationToken cancellationToken = default); Task UpdateAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default); diff --git a/src/Database/Api/Repositories/IVirtualApplicationEntityRepository.cs b/src/Database/Api/Repositories/IVirtualApplicationEntityRepository.cs new file mode 100644 index 000000000..542da9ba2 --- /dev/null +++ b/src/Database/Api/Repositories/IVirtualApplicationEntityRepository.cs @@ -0,0 +1,38 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Monai.Deploy.InformaticsGateway.Api; + +namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories +{ + public interface IVirtualApplicationEntityRepository + { + Task> ToListAsync(CancellationToken cancellationToken = default); + + Task FindByNameAsync(string name, CancellationToken cancellationToken = default); + + Task FindByAeTitleAsync(string aeTitle, CancellationToken cancellationToken = default); + + Task AddAsync(VirtualApplicationEntity item, CancellationToken cancellationToken = default); + + Task UpdateAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default); + + Task RemoveAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default); + + Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default); + } +} diff --git a/src/Database/Api/Repositories/InferenceRequestRepositoryBase.cs b/src/Database/Api/Repositories/InferenceRequestRepositoryBase.cs old mode 100644 new mode 100755 index cd0c8e6d1..8775eb688 --- a/src/Database/Api/Repositories/InferenceRequestRepositoryBase.cs +++ b/src/Database/Api/Repositories/InferenceRequestRepositoryBase.cs @@ -17,7 +17,6 @@ using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; @@ -27,11 +26,11 @@ namespace Monai.Deploy.InformaticsGateway.Database.Api.Repositories public abstract class InferenceRequestRepositoryBase : IInferenceRequestRepository { private readonly ILogger _logger; - private readonly IOptions _options; + private readonly IOptions _options; protected InferenceRequestRepositoryBase( ILogger logger, - IOptions options) + IOptions options) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _options = options ?? throw new ArgumentNullException(nameof(options)); @@ -39,13 +38,13 @@ protected InferenceRequestRepositoryBase( public virtual async Task ExistsAsync(string transactionId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(transactionId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); return await GetInferenceRequestAsync(transactionId, cancellationToken).ConfigureAwait(false) is not null; } public virtual async Task GetStatusAsync(string transactionId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(transactionId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); var response = new InferenceStatusResponse(); var item = await GetInferenceRequestAsync(transactionId, cancellationToken).ConfigureAwait(false); @@ -61,9 +60,9 @@ public virtual async Task ExistsAsync(string transactionId, CancellationTo public async Task UpdateAsync(InferenceRequest inferenceRequest, InferenceRequestStatus status, CancellationToken cancellationToken = default) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "TransactionId", inferenceRequest.TransactionId } }); + using var loggerScope = _logger.BeginScope(new InformaticsGateway.Api.LoggingDataDictionary { { "TransactionId", inferenceRequest.TransactionId } }); if (status == InferenceRequestStatus.Success) { @@ -72,7 +71,7 @@ public async Task UpdateAsync(InferenceRequest inferenceRequest, InferenceReques } else { - if (++inferenceRequest.TryCount > _options.Value.Database.Retries.DelaysMilliseconds.Length) + if (++inferenceRequest.TryCount > _options.Value.Retries.DelaysMilliseconds.Length) { _logger.InferenceRequestUpdateExceededMaximumRetries(); inferenceRequest.State = InferenceRequestState.Completed; diff --git a/src/Database/Api/Repositories/StorageMetadataRepositoryBase.cs b/src/Database/Api/Repositories/StorageMetadataRepositoryBase.cs old mode 100644 new mode 100755 index 5f9f06f4e..b56ba1464 --- a/src/Database/Api/Repositories/StorageMetadataRepositoryBase.cs +++ b/src/Database/Api/Repositories/StorageMetadataRepositoryBase.cs @@ -33,7 +33,7 @@ protected StorageMetadataRepositoryBase(ILogger logger) public async Task AddAsync(FileStorageMetadata metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", metadata.CorrelationId }, { "Identity", metadata.Id } }); var obj = new StorageMetadataWrapper(metadata); @@ -43,7 +43,7 @@ public async Task AddAsync(FileStorageMetadata metadata, CancellationToken cance public async Task AddOrUpdateAsync(FileStorageMetadata metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); var existing = await GetFileStorageMetdadataAsync(metadata.CorrelationId, metadata.Id, cancellationToken).ConfigureAwait(false); @@ -59,8 +59,8 @@ public async Task AddOrUpdateAsync(FileStorageMetadata metadata, CancellationTok public virtual async Task DeleteAsync(string correlationId, string identity, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(identity); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(identity, nameof(identity)); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", correlationId }, { "Identity", identity } }); @@ -73,7 +73,8 @@ public virtual async Task DeleteAsync(string correlationId, string identit return false; } - protected abstract Task DeleteInternalAsync(StorageMetadataWrapper toBeDeleted, CancellationToken cancellationToken); + protected abstract Task DeleteInternalAsync(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default); + public abstract Task DeletePendingUploadsAsync(CancellationToken cancellationToken = default); public abstract Task> GetFileStorageMetdadataAsync(string correlationId, CancellationToken cancellationToken = default); @@ -82,7 +83,7 @@ public virtual async Task DeleteAsync(string correlationId, string identit public virtual async Task UpdateAsync(FileStorageMetadata metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", metadata.CorrelationId }, { "Identity", metadata.Id } }); var obj = await FindByIds(metadata.Id, metadata.CorrelationId).ConfigureAwait(false); @@ -97,8 +98,10 @@ public virtual async Task UpdateAsync(FileStorageMetadata metadata, Cancellation _logger.StorageMetadataSaved(); } - protected abstract Task UpdateInternal(StorageMetadataWrapper obj, CancellationToken cancellationToken = default); + protected abstract Task UpdateInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default); + protected abstract Task FindByIds(string id, string correlationId, CancellationToken cancellationToken = default); + protected abstract Task AddAsyncInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default); } } diff --git a/src/Database/EntityFramework/Configuration/SR.cs b/src/Database/Api/SR.cs similarity index 88% rename from src/Database/EntityFramework/Configuration/SR.cs rename to src/Database/Api/SR.cs index ebcae9fc9..268b6f425 100644 --- a/src/Database/EntityFramework/Configuration/SR.cs +++ b/src/Database/Api/SR.cs @@ -15,7 +15,7 @@ * limitations under the License. */ -namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +namespace Monai.Deploy.InformaticsGateway.Database.Api { public static class SR { @@ -24,5 +24,6 @@ public static class SR /// public const string DatabaseConnectionStringKey = "InformaticsGatewayDatabase"; + public const string DatabaseNameKey = "DatabaseName"; } } diff --git a/src/Database/Api/StorageMetadataWrapper.cs b/src/Database/Api/StorageMetadataWrapper.cs old mode 100644 new mode 100755 index f572a2606..9338a1d29 --- a/src/Database/Api/StorageMetadataWrapper.cs +++ b/src/Database/Api/StorageMetadataWrapper.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using Ardalis.GuardClauses; -using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Api.Storage; namespace Monai.Deploy.InformaticsGateway.Database.Api @@ -28,6 +26,7 @@ namespace Monai.Deploy.InformaticsGateway.Database.Api /// public class StorageMetadataWrapper : MongoDBEntityBase { + [JsonPropertyName("correlationId")] public string CorrelationId { get; set; } = string.Empty; @@ -43,12 +42,13 @@ public class StorageMetadataWrapper : MongoDBEntityBase [JsonPropertyName("isUploaded")] public bool IsUploaded { get; set; } + [JsonConstructor] private StorageMetadataWrapper() { } public StorageMetadataWrapper(FileStorageMetadata metadata) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); CorrelationId = metadata.CorrelationId; Identity = metadata.Id; @@ -57,10 +57,12 @@ public StorageMetadataWrapper(FileStorageMetadata metadata) public void Update(FileStorageMetadata metadata) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); IsUploaded = metadata.IsUploaded; - Value = JsonSerializer.Serialize(metadata); // Must be here + + //Value = JsonSerializer.Serialize(metadata); // Must be here + Value = JsonSerializer.Serialize(metadata, metadata.GetType()); if (metadata.GetType() is null || string.IsNullOrWhiteSpace(metadata.GetType().AssemblyQualifiedName)) { @@ -88,4 +90,4 @@ public override string ToString() return $"Identity: {Identity}"; } } -} +} \ No newline at end of file diff --git a/src/Database/Api/Test/Monai.Deploy.InformaticsGateway.Database.Api.Test.csproj b/src/Database/Api/Test/Monai.Deploy.InformaticsGateway.Database.Api.Test.csproj index 7ae3ec3e5..7f67ac038 100644 --- a/src/Database/Api/Test/Monai.Deploy.InformaticsGateway.Database.Api.Test.csproj +++ b/src/Database/Api/Test/Monai.Deploy.InformaticsGateway.Database.Api.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 enable enable false true - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + \ No newline at end of file diff --git a/src/Database/EntityFramework/Test/StorageMetadataWrapperTest.cs b/src/Database/Api/Test/StorageMetadataWrapperTest.cs similarity index 80% rename from src/Database/EntityFramework/Test/StorageMetadataWrapperTest.cs rename to src/Database/Api/Test/StorageMetadataWrapperTest.cs index 9a27a8ae7..252efec9a 100644 --- a/src/Database/EntityFramework/Test/StorageMetadataWrapperTest.cs +++ b/src/Database/Api/Test/StorageMetadataWrapperTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,9 @@ using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; -using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.Messaging.Events; -namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +namespace Monai.Deploy.InformaticsGateway.Database.Api.Test { public class StorageMetadataWrapperTest { @@ -29,7 +29,9 @@ public void GivenAFhirFileStorageMetadataObject_WhenInitializedWithStorageMetada Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - FhirStorageFormat.Json); + FhirStorageFormat.Json, + DataService.ACR, + "somewhere"); metadata.SetWorkflows("A", "B", "C"); var wrapper = new StorageMetadataWrapper(metadata); @@ -47,8 +49,9 @@ public void GivenAFhirFileStorageMetadataObject_WhenInitializedWithStorageMetada Assert.Equal(metadata.IsUploaded, unwrapped.IsUploaded); Assert.Equal(metadata.ResourceId, unwrapped.ResourceId); Assert.Equal(metadata.ResourceType, unwrapped.ResourceType); - Assert.Equal(metadata.Source, unwrapped.Source); - Assert.Equal(metadata.TransactionId, unwrapped.TransactionId); + Assert.Equal(metadata.DataOrigin.DataService, unwrapped.DataOrigin.DataService); + Assert.Equal(metadata.DataOrigin.Source, unwrapped.DataOrigin.Source); + Assert.Equal(metadata.DataOrigin.Destination, unwrapped.DataOrigin.Destination); Assert.Equal(metadata.Workflows, unwrapped.Workflows); Assert.Equal(metadata.File.FileExtension, unwrapped.File.FileExtension); @@ -66,12 +69,14 @@ public void GivenADicomFileStorageMetadataObject_WhenInitializedWithStorageMetad Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()) - { - CalledAeTitle = "CALLEDAET", - Source = "SOURCE", - }; + Guid.NewGuid().ToString(), + DataService.DIMSE, + "SOURCE" +, + "CALLEDAET"); metadata.SetWorkflows("A", "B", "C"); + metadata.WorkflowInstanceId = Guid.NewGuid().ToString(); + metadata.TaskId = Guid.NewGuid().ToString(); var wrapper = new StorageMetadataWrapper(metadata); @@ -81,8 +86,9 @@ public void GivenADicomFileStorageMetadataObject_WhenInitializedWithStorageMetad var unwrapped = wrapper.GetObject() as DicomFileStorageMetadata; - Assert.Equal(metadata.CalledAeTitle, unwrapped!.CalledAeTitle); - Assert.Equal(metadata.CallingAeTitle, unwrapped.CallingAeTitle); + Assert.Equal(metadata.DataOrigin.Source, unwrapped!.DataOrigin.Source); + Assert.Equal(metadata.DataOrigin.Destination, unwrapped!.DataOrigin.Destination); + Assert.Equal(metadata.DataOrigin.DataService, unwrapped!.DataOrigin.DataService); Assert.Equal(metadata.CorrelationId, unwrapped.CorrelationId); Assert.Equal(metadata.Id, unwrapped.Id); Assert.Equal(metadata.DataTypeDirectoryName, unwrapped.DataTypeDirectoryName); @@ -91,8 +97,9 @@ public void GivenADicomFileStorageMetadataObject_WhenInitializedWithStorageMetad Assert.Equal(metadata.SeriesInstanceUid, unwrapped.SeriesInstanceUid); Assert.Equal(metadata.SopInstanceUid, unwrapped.SopInstanceUid); Assert.Equal(metadata.StudyInstanceUid, unwrapped.StudyInstanceUid); - Assert.Equal(metadata.Source, unwrapped.Source); Assert.Equal(metadata.Workflows, unwrapped.Workflows); + Assert.Equal(metadata.WorkflowInstanceId, unwrapped.WorkflowInstanceId); + Assert.Equal(metadata.TaskId, unwrapped.TaskId); Assert.Equal(metadata.File.FileExtension, unwrapped.File.FileExtension); Assert.Equal(metadata.File.UploadPath, unwrapped.File.UploadPath); @@ -107,4 +114,4 @@ public void GivenADicomFileStorageMetadataObject_WhenInitializedWithStorageMetad Assert.Equal(metadata.JsonFile.ContentType, unwrapped.JsonFile.ContentType); } } -} +} \ No newline at end of file diff --git a/src/Database/Api/Test/packages.lock.json b/src/Database/Api/Test/packages.lock.json index 74ab6cca6..3e736c8bb 100644 --- a/src/Database/Api/Test/packages.lock.json +++ b/src/Database/Api/Test/packages.lock.json @@ -1,81 +1,85 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -84,73 +88,30 @@ }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.CodeCoverage": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.EntityFrameworkCore": { + "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { @@ -164,41 +125,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -215,285 +187,114 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", - "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "NuGet.Frameworks": { + "NLog": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "Polly": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "Polly.Core": "8.5.2" } }, - "runtime.native.System.IO.Compression": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Net.Http": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "System.Buffers": { @@ -501,68 +302,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -571,686 +310,61 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.Compression.ZipFile": { + "System.Memory": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Timer": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1260,82 +374,72 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } } } diff --git a/src/Database/Api/packages.lock.json b/src/Database/Api/packages.lock.json index 1b8af4b28..f4ce0b6c2 100644 --- a/src/Database/Api/packages.lock.json +++ b/src/Database/Api/packages.lock.json @@ -1,69 +1,58 @@ { "version": 1, "dependencies": { - "net6.0": { - "Microsoft.EntityFrameworkCore": { + "net8.0": { + "NLog": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } - }, - "Polly": { - "type": "Direct", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[5.4.0, )", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -72,54 +61,25 @@ }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" - }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.Extensions.Caching.Memory": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.Extensions.Configuration": { + "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { @@ -133,41 +93,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -184,95 +155,98 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Toolkit.HighPerformance": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "System.Buffers": { + "Polly": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } }, - "System.Collections.Immutable": { + "Polly.Core": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, - "System.ComponentModel.Annotations": { + "System.Buffers": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", @@ -284,8 +258,17 @@ }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -294,61 +277,62 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } } } diff --git a/src/Database/DatabaseManager.cs b/src/Database/DatabaseManager.cs old mode 100644 new mode 100755 index 1aafa7666..e04199c0d --- a/src/Database/DatabaseManager.cs +++ b/src/Database/DatabaseManager.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,23 @@ * limitations under the License. */ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using Ardalis.GuardClauses; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Database.EntityFramework; -using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration; using Monai.Deploy.InformaticsGateway.Database.MongoDB; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; +using ConfigurationException = Monai.Deploy.InformaticsGateway.Configuration.ConfigurationException; namespace Monai.Deploy.InformaticsGateway.Database { @@ -32,15 +38,44 @@ public static class DatabaseManager { public const string DbType_Sqlite = "sqlite"; public const string DbType_MongoDb = "mongodb"; - public static IServiceCollection ConfigureDatabase(this IServiceCollection services, IConfigurationSection? connectionStringConfigurationSection) + + + public static IHealthChecksBuilder AddDatabaseHealthCheck(this IHealthChecksBuilder healthChecksBuilder, IConfigurationSection? connectionStringConfigurationSection) { if (connectionStringConfigurationSection is null) { throw new ConfigurationException("No database connections found in configuration section 'ConnectionStrings'."); } + var databaseType = connectionStringConfigurationSection!["Type"]!.ToLowerInvariant(); + + switch (databaseType) + { + case DbType_Sqlite: + healthChecksBuilder.AddDbContextCheck("SQLite Database"); + return healthChecksBuilder; + + case DbType_MongoDb: + healthChecksBuilder.AddMongoDb(mongodbConnectionString: connectionStringConfigurationSection[SR.DatabaseConnectionStringKey]!, mongoDatabaseName: connectionStringConfigurationSection[SR.DatabaseNameKey]!, name: "MongoDB"); + return healthChecksBuilder; + + default: + throw new ConfigurationException($"Unsupported database type defined: '{databaseType}'"); + } + } + + public static IServiceCollection ConfigureDatabase(this IServiceCollection services, IConfigurationSection? connectionStringConfigurationSection, IConfigurationSection? pluginsConfigurationSection, ILoggerFactory loggerFactory) + => services.ConfigureDatabase(connectionStringConfigurationSection, pluginsConfigurationSection, new FileSystem(), loggerFactory); + + public static IServiceCollection ConfigureDatabase(this IServiceCollection services, IConfigurationSection? connectionStringConfigurationSection, IConfigurationSection? pluginsConfigurationSection, IFileSystem fileSystem, ILoggerFactory loggerFactory) + { - var databaseType = connectionStringConfigurationSection["Type"].ToLowerInvariant(); + if (connectionStringConfigurationSection is null) + { + throw new ConfigurationException("No database connections found in configuration section 'ConnectionStrings'."); + } + services.Configure(connectionStringConfigurationSection.GetSection("DatabaseOptions")); + var databaseType = connectionStringConfigurationSection["Type"]!.ToLowerInvariant(); switch (databaseType) { case DbType_Sqlite: @@ -49,27 +84,101 @@ public static IServiceCollection ConfigureDatabase(this IServiceCollection servi ServiceLifetime.Transient); services.AddScoped(); services.AddScoped(typeof(IDestinationApplicationEntityRepository), typeof(EntityFramework.Repositories.DestinationApplicationEntityRepository)); + services.AddScoped(typeof(IHL7DestinationEntityRepository), typeof(EntityFramework.Repositories.HL7DestinationEntityRepository)); services.AddScoped(typeof(IInferenceRequestRepository), typeof(EntityFramework.Repositories.InferenceRequestRepository)); services.AddScoped(typeof(IMonaiApplicationEntityRepository), typeof(EntityFramework.Repositories.MonaiApplicationEntityRepository)); services.AddScoped(typeof(ISourceApplicationEntityRepository), typeof(EntityFramework.Repositories.SourceApplicationEntityRepository)); services.AddScoped(typeof(IStorageMetadataRepository), typeof(EntityFramework.Repositories.StorageMetadataWrapperRepository)); services.AddScoped(typeof(IPayloadRepository), typeof(EntityFramework.Repositories.PayloadRepository)); + services.AddScoped(typeof(IDicomAssociationInfoRepository), typeof(EntityFramework.Repositories.DicomAssociationInfoRepository)); + services.AddScoped(typeof(IVirtualApplicationEntityRepository), typeof(EntityFramework.Repositories.VirtualApplicationEntityRepository)); + services.AddScoped(typeof(IHl7ApplicationConfigRepository), typeof(EntityFramework.Repositories.Hl7ApplicationConfigRepository)); + services.AddSingleton(typeof(IExternalAppDetailsRepository), typeof(EntityFramework.Repositories.ExternalAppDetailsRepository)); + + services.ConfigureDatabaseFromPlugIns(DatabaseType.EntityFramework, fileSystem, connectionStringConfigurationSection, pluginsConfigurationSection, loggerFactory); return services; + case DbType_MongoDb: services.AddSingleton(s => new MongoClient(connectionStringConfigurationSection[SR.DatabaseConnectionStringKey])); - services.Configure(connectionStringConfigurationSection); services.AddScoped(); services.AddScoped(typeof(IDestinationApplicationEntityRepository), typeof(MongoDB.Repositories.DestinationApplicationEntityRepository)); + services.AddScoped(typeof(IHL7DestinationEntityRepository), typeof(MongoDB.Repositories.HL7DestinationEntityRepository)); services.AddScoped(typeof(IInferenceRequestRepository), typeof(MongoDB.Repositories.InferenceRequestRepository)); services.AddScoped(typeof(IMonaiApplicationEntityRepository), typeof(MongoDB.Repositories.MonaiApplicationEntityRepository)); services.AddScoped(typeof(ISourceApplicationEntityRepository), typeof(MongoDB.Repositories.SourceApplicationEntityRepository)); services.AddScoped(typeof(IStorageMetadataRepository), typeof(MongoDB.Repositories.StorageMetadataWrapperRepository)); services.AddScoped(typeof(IPayloadRepository), typeof(MongoDB.Repositories.PayloadRepository)); + services.AddScoped(typeof(IDicomAssociationInfoRepository), typeof(MongoDB.Repositories.DicomAssociationInfoRepository)); + services.AddScoped(typeof(IVirtualApplicationEntityRepository), typeof(MongoDB.Repositories.VirtualApplicationEntityRepository)); + services.AddScoped(typeof(IHl7ApplicationConfigRepository), typeof(MongoDB.Repositories.Hl7ApplicationConfigRepository)); + services.AddSingleton(typeof(IExternalAppDetailsRepository), typeof(MongoDB.Repositories.ExternalAppDetailsRepository)); + + services.ConfigureDatabaseFromPlugIns(DatabaseType.MongoDb, fileSystem, connectionStringConfigurationSection, pluginsConfigurationSection, loggerFactory); return services; + default: throw new ConfigurationException($"Unsupported database type defined: '{databaseType}'"); } } + + public static IServiceCollection ConfigureDatabaseFromPlugIns(this IServiceCollection services, + DatabaseType databaseType, + IFileSystem fileSystem, + IConfigurationSection? connectionStringConfigurationSection, + IConfigurationSection? pluginsConfigurationSection, + ILoggerFactory loggerFactory) + { + Guard.Against.Null(fileSystem, nameof(fileSystem)); + + var assemblies = LoadAssemblyFromPlugInsDirectory(fileSystem); + var matchingTypes = FindMatchingTypesFromAssemblies(assemblies); + + foreach (var type in matchingTypes) + { + if (Activator.CreateInstance(type) is not DatabaseRegistrationBase registrar) + { + throw new ConfigurationException($"Error activating database registration from type '{type.FullName}'."); + } + registrar.Configure(services, databaseType, connectionStringConfigurationSection, pluginsConfigurationSection, loggerFactory); + } + return services; + } + + internal static Type[] FindMatchingTypesFromAssemblies(Assembly[] assemblies) + { + var matchingTypes = new List(); + foreach (var assembly in assemblies) + { + var types = assembly.ExportedTypes.Where(p => p.IsSubclassOf(typeof(DatabaseRegistrationBase))); + if (types.Any()) + { + matchingTypes.AddRange(types); + } + } + + return matchingTypes.ToArray(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("SonarLint", "S3885", Justification = "assembly.Load does not full register the contents, but assembly.LoadFrom does, this is need to load .dlls from the plug-ins folder that plugins use")] + internal static Assembly[] LoadAssemblyFromPlugInsDirectory(IFileSystem fileSystem) + { + Guard.Against.Null(fileSystem, nameof(fileSystem)); + + if (!fileSystem.Directory.Exists(InformaticsGateway.Api.PlugIns.SR.PlugInDirectoryPath)) + { + throw new ConfigurationException($"Plug-in directory '{InformaticsGateway.Api.PlugIns.SR.PlugInDirectoryPath}' cannot be found."); + } + + var assemblies = new List(); + var plugins = fileSystem.Directory.GetFiles(InformaticsGateway.Api.PlugIns.SR.PlugInDirectoryPath, "*.dll"); + + foreach (var plugin in plugins) + { + + assemblies.Add(Assembly.LoadFrom(plugin)); + } + return assemblies.ToArray(); + } } } diff --git a/src/Database/DatabaseMigrationManager.cs b/src/Database/DatabaseMigrationManager.cs old mode 100644 new mode 100755 index 2d27d194c..2a81aa1e5 --- a/src/Database/DatabaseMigrationManager.cs +++ b/src/Database/DatabaseMigrationManager.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,14 @@ * limitations under the License. */ +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api; namespace Monai.Deploy.InformaticsGateway.Database @@ -26,10 +32,43 @@ public static IHost MigrateDatabase(this IHost host) { using (var scope = host.Services.CreateScope()) { - scope.ServiceProvider.GetService()?.Migrate(host); + scope.ServiceProvider.GetRequiredService()?.Migrate(host); + var fileSystem = scope.ServiceProvider.GetRequiredService(); + + host.MigrateDatabaseFromExternalPlugIns(fileSystem); + } + return host; + } + + private static IHost MigrateDatabaseFromExternalPlugIns(this IHost host, IFileSystem fileSystem) + { + var assemblies = DatabaseManager.LoadAssemblyFromPlugInsDirectory(fileSystem); + var matchingTypes = FindMatchingTypesFromAssemblies(assemblies); + + foreach (var type in matchingTypes) + { + if (Activator.CreateInstance(type) is not IDatabaseMigrationManager migrationManager) + { + throw new ConfigurationException($"Error activating IDatabaseMigrationManager from type '{type.FullName}'."); + } + migrationManager.Migrate(host); } return host; } + + private static Type[] FindMatchingTypesFromAssemblies(Assembly[] assemblies) + { + var matchingTypes = new List(); + foreach (var assembly in assemblies) + { + var types = assembly.ExportedTypes.Where(p => p.IsAssignableFrom(typeof(IDatabaseMigrationManager)) && p.Name != nameof(IDatabaseMigrationManager)); + if (types.Any()) + { + matchingTypes.AddRange(types); + } + } + + return [.. matchingTypes]; + } } } - diff --git a/src/Database/EntityFramework/Configuration/DestinationApplicationEntityConfiguration.cs b/src/Database/EntityFramework/Configuration/DestinationApplicationEntityConfiguration.cs old mode 100644 new mode 100755 index 98b990120..33d7ff2f5 --- a/src/Database/EntityFramework/Configuration/DestinationApplicationEntityConfiguration.cs +++ b/src/Database/EntityFramework/Configuration/DestinationApplicationEntityConfiguration.cs @@ -17,7 +17,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration { @@ -29,6 +29,10 @@ public void Configure(EntityTypeBuilder builder) builder.Property(j => j.AeTitle).IsRequired(); builder.Property(j => j.Port).IsRequired(); builder.Property(j => j.HostIp).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.UpdatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.DateTimeUpdated).IsRequired(false); builder.HasIndex(p => p.Name, "idx_destination_name").IsUnique(); builder.HasIndex(p => new { p.Name, p.AeTitle, p.HostIp, p.Port }, "idx_source_all").IsUnique(); diff --git a/src/Database/EntityFramework/Configuration/DicomAssociationInfoConfiguration.cs b/src/Database/EntityFramework/Configuration/DicomAssociationInfoConfiguration.cs new file mode 100755 index 000000000..d8b2c45f5 --- /dev/null +++ b/src/Database/EntityFramework/Configuration/DicomAssociationInfoConfiguration.cs @@ -0,0 +1,57 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +{ + internal class DicomAssociationInfoConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var comparer = new ValueComparer>( + (c1, c2) => c1!.SequenceEqual(c2!), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToHashSet()); + + var jsonSerializerSettings = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + builder.HasKey(j => j.Id); + builder.Property(j => j.CalledAeTitle).IsRequired(); + builder.Property(j => j.CalledAeTitle).IsRequired(); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.DateTimeDisconnected).IsRequired(); + builder.Property(j => j.CorrelationId).IsRequired(); + builder.Property(j => j.FileCount).IsRequired(); + builder.Property(j => j.RemoteHost).IsRequired(); + builder.Property(j => j.RemotePort).IsRequired(); + builder.Property(j => j.Errors).IsRequired(); + builder.Property(j => j.PayloadIds).IsRequired() + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings) ?? new HashSet()) + .Metadata.SetValueComparer(comparer); + } + } +} diff --git a/src/Database/EntityFramework/Configuration/ExternalAppDetailsConfiguration.cs b/src/Database/EntityFramework/Configuration/ExternalAppDetailsConfiguration.cs new file mode 100755 index 000000000..cfdcf8d9d --- /dev/null +++ b/src/Database/EntityFramework/Configuration/ExternalAppDetailsConfiguration.cs @@ -0,0 +1,44 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +using System.Text.Json.Serialization; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +{ + internal class ExternalAppDetailsConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var jsonSerializerSettings = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + builder.HasKey(j => j.Id); + + builder.Property(j => j.StudyInstanceUid).IsRequired(); + builder.Property(j => j.WorkflowInstanceId).IsRequired(); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.CorrelationId).IsRequired(); + builder.Property(j => j.ExportTaskID).IsRequired(); + } + } +} diff --git a/src/Database/EntityFramework/Configuration/HL7DestinationEntityConfiguration.cs b/src/Database/EntityFramework/Configuration/HL7DestinationEntityConfiguration.cs new file mode 100755 index 000000000..8bffd2a6d --- /dev/null +++ b/src/Database/EntityFramework/Configuration/HL7DestinationEntityConfiguration.cs @@ -0,0 +1,42 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +{ + internal class HL7DestinationEntityConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(j => j.Name); + builder.Property(j => j.Port).IsRequired(); + builder.Property(j => j.HostIp).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.UpdatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.DateTimeUpdated).IsRequired(false); + + builder.HasIndex(p => p.Name, "idx_destination_name").IsUnique(); + builder.HasIndex(p => new { p.Name, p.HostIp, p.Port }, "idx_source_all").IsUnique(); + + builder.Ignore(p => p.Id); + } + } +} diff --git a/src/Database/EntityFramework/Configuration/Hl7ApplicationConfigConfiguration.cs b/src/Database/EntityFramework/Configuration/Hl7ApplicationConfigConfiguration.cs new file mode 100755 index 000000000..cfd9a186b --- /dev/null +++ b/src/Database/EntityFramework/Configuration/Hl7ApplicationConfigConfiguration.cs @@ -0,0 +1,86 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json.Serialization; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Monai.Deploy.InformaticsGateway.Api; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +{ + internal class Hl7ApplicationConfigConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var valueComparer = new ValueComparer>( + (c1, c2) => c1!.SequenceEqual(c2!), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToList()); + + var jsonSerializerSettings = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + builder.HasKey(j => j.Name); + builder.Property(j => j.SendingId).HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize(v, jsonSerializerSettings)!) + .IsRequired() + .Metadata + .SetValueComparer( + new ValueComparer( + (c1, c2) => c1 == c2, + c => c.GetHashCode(), + c => c)); + + builder.Property(j => j.DataLink).HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize(v, jsonSerializerSettings)!) + .IsRequired() + .Metadata + .SetValueComparer( + new ValueComparer( + (c1, c2) => c1 == c2, + c => c.GetHashCode(), + c => c)); + + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.PlugInAssemblies) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)!) + .Metadata.SetValueComparer(valueComparer); + + builder.Property(j => j.DataMapping) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)!) + .Metadata + .SetValueComparer( + new ValueComparer>( + (c1, c2) => c1!.SequenceEqual(c2!), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToList())); + + builder.HasIndex(p => p.Name, "idx_hl7_name").IsUnique(); + + builder.Ignore(p => p.Id); + } + } +} diff --git a/src/Database/EntityFramework/Configuration/InferenceRequestConfiguration.cs b/src/Database/EntityFramework/Configuration/InferenceRequestConfiguration.cs index cbaf3385d..ba58ba66e 100644 --- a/src/Database/EntityFramework/Configuration/InferenceRequestConfiguration.cs +++ b/src/Database/EntityFramework/Configuration/InferenceRequestConfiguration.cs @@ -26,6 +26,7 @@ namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration { #pragma warning disable CS8603 // Possible null reference return. #pragma warning disable CS8604 // Possible null reference argument. + internal class InferenceRequestConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) @@ -50,16 +51,16 @@ public void Configure(EntityTypeBuilder builder) builder.Property(j => j.TransactionId).IsRequired(); builder.Property(j => j.Priority).IsRequired(); - builder.Property(j => j.InputMetadata).HasConversion( + builder.Property(j => j.InputMetadata).IsRequired(false).HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), v => JsonSerializer.Deserialize(v, jsonSerializerSettings)); - builder.Property(j => j.InputResources).HasConversion( + builder.Property(j => j.InputResources).IsRequired(false).HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) .Metadata.SetValueComparer(reqestInputResourceComparer); - builder.Property(j => j.OutputResources).HasConversion( + builder.Property(j => j.OutputResources).IsRequired(false).HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) .Metadata.SetValueComparer(reqestOutputResourceComparer); @@ -67,6 +68,8 @@ public void Configure(EntityTypeBuilder builder) builder.Property(j => j.State).IsRequired(); builder.Property(j => j.Status).IsRequired(); builder.Property(j => j.TryCount).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); builder.Ignore(p => p.Application); @@ -75,6 +78,7 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(p => p.TransactionId, "idx_inferencerequest_transactionid").IsUnique(); } } + #pragma warning restore CS8604 // Possible null reference argument. #pragma warning restore CS8603 // Possible null reference return. } diff --git a/src/Database/EntityFramework/Configuration/MonaiApplicationEntityConfiguration.cs b/src/Database/EntityFramework/Configuration/MonaiApplicationEntityConfiguration.cs old mode 100644 new mode 100755 index db60b79cc..fc9f1666c --- a/src/Database/EntityFramework/Configuration/MonaiApplicationEntityConfiguration.cs +++ b/src/Database/EntityFramework/Configuration/MonaiApplicationEntityConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,19 +15,17 @@ * limitations under the License. */ -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration { #pragma warning disable CS8604, CS8603 + internal class MonaiApplicationEntityConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) @@ -47,11 +45,18 @@ public void Configure(EntityTypeBuilder builder) builder.Property(j => j.AeTitle).IsRequired(); builder.Property(j => j.Timeout).IsRequired(); builder.Property(j => j.Grouping).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); builder.Property(j => j.Workflows) .HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) .Metadata.SetValueComparer(valueComparer); + builder.Property(j => j.PlugInAssemblies) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) + .Metadata.SetValueComparer(valueComparer); builder.Property(j => j.IgnoredSopClasses) .HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), @@ -68,5 +73,6 @@ public void Configure(EntityTypeBuilder builder) builder.Ignore(p => p.Id); } } + #pragma warning restore CS8604, CS8603 } diff --git a/src/Database/EntityFramework/Configuration/PayloadConfiguration.cs b/src/Database/EntityFramework/Configuration/PayloadConfiguration.cs index 71df8d733..2b4d869b3 100644 --- a/src/Database/EntityFramework/Configuration/PayloadConfiguration.cs +++ b/src/Database/EntityFramework/Configuration/PayloadConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,12 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration { #pragma warning disable CS8604, CS8603 + internal class PayloadConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) @@ -32,6 +34,10 @@ public void Configure(EntityTypeBuilder builder) (c1, c2) => c1.SequenceEqual(c2), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => c.ToList()); + var dataOriginsComparer = new ValueComparer>( + (c1, c2) => c1.SequenceEqual(c2), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToHashSet()); var jsonSerializerSettings = new JsonSerializerOptions { @@ -46,14 +52,21 @@ public void Configure(EntityTypeBuilder builder) builder.Property(j => j.RetryCount).IsRequired(); builder.Property(j => j.State).IsRequired(); builder.Property(j => j.CorrelationId).IsRequired(); + builder.Property(j => j.DataTrigger).IsRequired().HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize(v, jsonSerializerSettings)); + builder.Property(j => j.MachineName); builder.Property(j => j.Files) .HasConversion( v => JsonSerializer.Serialize(v, jsonSerializerSettings), v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) .Metadata.SetValueComparer(metadataComparer); + builder.Property(j => j.DataOrigins) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) + .Metadata.SetValueComparer(dataOriginsComparer); - builder.Ignore(j => j.CalledAeTitle); - builder.Ignore(j => j.CallingAeTitle); builder.Ignore(j => j.HasTimedOut); builder.Ignore(j => j.Elapsed); builder.Ignore(j => j.Count); @@ -62,5 +75,6 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(p => new { p.CorrelationId, p.PayloadId }, "idx_payload_ids").IsUnique(); } } + #pragma warning restore CS8604, CS8603 -} +} \ No newline at end of file diff --git a/src/Database/EntityFramework/Configuration/SourceApplicationEntityConfiguration.cs b/src/Database/EntityFramework/Configuration/SourceApplicationEntityConfiguration.cs index 863798216..c26824b6a 100644 --- a/src/Database/EntityFramework/Configuration/SourceApplicationEntityConfiguration.cs +++ b/src/Database/EntityFramework/Configuration/SourceApplicationEntityConfiguration.cs @@ -27,6 +27,10 @@ public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityType builder.HasKey(j => j.Name); builder.Property(j => j.AeTitle).IsRequired(); builder.Property(j => j.HostIp).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.UpdatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.DateTimeUpdated).IsRequired(false); builder.HasIndex(p => p.Name, "idx_source_name").IsUnique(); builder.HasIndex(p => new { p.Name, p.AeTitle, p.HostIp }, "idx_source_all").IsUnique(); diff --git a/src/Database/EntityFramework/Configuration/VirtualApplicationEntityConfiguration.cs b/src/Database/EntityFramework/Configuration/VirtualApplicationEntityConfiguration.cs new file mode 100644 index 000000000..3d52e751d --- /dev/null +++ b/src/Database/EntityFramework/Configuration/VirtualApplicationEntityConfiguration.cs @@ -0,0 +1,65 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Monai.Deploy.InformaticsGateway.Api; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration +{ +#pragma warning disable CS8604, CS8603 + + internal class VirtualApplicationEntityConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var valueComparer = new ValueComparer>( + (c1, c2) => c1.SequenceEqual(c2), + c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), + c => c.ToList()); + + var jsonSerializerSettings = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + builder.HasKey(j => j.Name); + + builder.Property(j => j.VirtualAeTitle).IsRequired(); + builder.Property(j => j.CreatedBy).IsRequired(false); + builder.Property(j => j.DateTimeCreated).IsRequired(); + builder.Property(j => j.Workflows) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) + .Metadata.SetValueComparer(valueComparer); + builder.Property(j => j.PlugInAssemblies) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) + .Metadata.SetValueComparer(valueComparer); + + builder.HasIndex(p => p.Name, "idx_virtualae_name").IsUnique(); + + builder.Ignore(p => p.Id); + } + } + +#pragma warning restore CS8604, CS8603 +} diff --git a/src/Database/EntityFramework/EfDatabaseMigrationManager.cs b/src/Database/EntityFramework/EfDatabaseMigrationManager.cs old mode 100644 new mode 100755 index edbd87511..5c90d9199 --- a/src/Database/EntityFramework/EfDatabaseMigrationManager.cs +++ b/src/Database/EntityFramework/EfDatabaseMigrationManager.cs @@ -37,7 +37,7 @@ public IHost Migrate(IHost host) catch (Exception ex) { var logger = scope.ServiceProvider.GetService(); - logger?.Log(LogLevel.Critical, "Failed to migrate database", ex); + logger?.Log(LogLevel.Critical, message: "Failed to migrate database", exception: ex); throw; } } diff --git a/src/Database/EntityFramework/InformaticsGatewayContext.cs b/src/Database/EntityFramework/InformaticsGatewayContext.cs old mode 100644 new mode 100755 index acd209d63..e77685ee1 --- a/src/Database/EntityFramework/InformaticsGatewayContext.cs +++ b/src/Database/EntityFramework/InformaticsGatewayContext.cs @@ -18,6 +18,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Database.Api; @@ -25,7 +26,6 @@ namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework { - #pragma warning disable CS8618 // Unread "private" fields should be removed public class InformaticsGatewayContext : DbContext @@ -37,9 +37,18 @@ public InformaticsGatewayContext(DbContextOptions opt public virtual DbSet MonaiApplicationEntities { get; set; } public virtual DbSet SourceApplicationEntities { get; set; } public virtual DbSet DestinationApplicationEntities { get; set; } + public virtual DbSet HL7DestinationEntities { get; set; } public virtual DbSet InferenceRequests { get; set; } public virtual DbSet Payloads { get; set; } public virtual DbSet StorageMetadataWrapperEntities { get; set; } + public virtual DbSet DicomAssociationHistories { get; set; } + public virtual DbSet VirtualApplicationEntities { get; set; } + public virtual DbSet ExternalAppDetails { get; set; } + + public virtual DbSet Hl7ApplicationConfig { get; set; } + + public virtual DbSet Hl7ApplicationConfig { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -48,9 +57,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.ApplyConfiguration(new MonaiApplicationEntityConfiguration()); modelBuilder.ApplyConfiguration(new SourceApplicationEntityConfiguration()); modelBuilder.ApplyConfiguration(new DestinationApplicationEntityConfiguration()); + modelBuilder.ApplyConfiguration(new HL7DestinationEntityConfiguration()); modelBuilder.ApplyConfiguration(new InferenceRequestConfiguration()); modelBuilder.ApplyConfiguration(new PayloadConfiguration()); modelBuilder.ApplyConfiguration(new StorageMetadataWrapperEntityConfiguration()); + modelBuilder.ApplyConfiguration(new DicomAssociationInfoConfiguration()); + modelBuilder.ApplyConfiguration(new VirtualApplicationEntityConfiguration()); + modelBuilder.ApplyConfiguration(new Hl7ApplicationConfigConfiguration()); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -81,5 +94,6 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) (RelationalEventId.CommandCreated, LogLevel.Trace) )); } + #pragma warning restore CS8618 // Unread "private" fields should be removed } diff --git a/src/Database/EntityFramework/InformaticsGatewayContextFactory.cs b/src/Database/EntityFramework/InformaticsGatewayContextFactory.cs index f623c0c2c..efdbb455b 100644 --- a/src/Database/EntityFramework/InformaticsGatewayContextFactory.cs +++ b/src/Database/EntityFramework/InformaticsGatewayContextFactory.cs @@ -18,7 +18,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; -using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework { diff --git a/src/Database/EntityFramework/Migrations/20220203222116_R1_Initialize.cs b/src/Database/EntityFramework/Migrations/20220203222116_R1_Initialize.cs index 177c6752c..4786c8811 100644 --- a/src/Database/EntityFramework/Migrations/20220203222116_R1_Initialize.cs +++ b/src/Database/EntityFramework/Migrations/20220203222116_R1_Initialize.cs @@ -14,7 +14,6 @@ * limitations under the License. */ -using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/Database/EntityFramework/Migrations/20221010184458_R3_0.3.2.cs b/src/Database/EntityFramework/Migrations/20221010184458_R3_0.3.2.cs old mode 100644 new mode 100755 index 92ad58af7..272743fe2 --- a/src/Database/EntityFramework/Migrations/20221010184458_R3_0.3.2.cs +++ b/src/Database/EntityFramework/Migrations/20221010184458_R3_0.3.2.cs @@ -8,12 +8,12 @@ public partial class R3_032 : Migration { protected override void Up(MigrationBuilder migrationBuilder) { - + // know that this is empty, can it be removed ? } protected override void Down(MigrationBuilder migrationBuilder) { - + // know that this is empty, can it be removed ? } } } diff --git a/src/Database/EntityFramework/Migrations/20221116172042_R3_0.3.4.cs b/src/Database/EntityFramework/Migrations/20221116172042_R3_0.3.4.cs old mode 100644 new mode 100755 index 007e9c291..edb12d348 --- a/src/Database/EntityFramework/Migrations/20221116172042_R3_0.3.4.cs +++ b/src/Database/EntityFramework/Migrations/20221116172042_R3_0.3.4.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -7,6 +6,9 @@ namespace Monai.Deploy.InformaticsGateway.Database.Migrations { public partial class R3_034 : Migration { + private static readonly string[] StorageMetadataWrapperEntitiesColumns = ["CorrelationId", "Identity"]; + private static readonly string[] StorageMetadataWrapperColumns = ["CorrelationId", "Identity"]; + protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( @@ -88,7 +90,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "idx_storagemetadata_ids", table: "StorageMetadataWrapperEntities", - columns: new[] { "CorrelationId", "Identity" }); + columns: StorageMetadataWrapperEntitiesColumns); migrationBuilder.CreateIndex( name: "idx_storagemetadata_uploaded", @@ -167,7 +169,7 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "idx_storagemetadata_ids", table: "StorageMetadataWrapper", - columns: new[] { "CorrelationId", "Identity" }); + columns: StorageMetadataWrapperColumns); migrationBuilder.CreateIndex( name: "idx_storagemetadata_uploaded", diff --git a/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.Designer.cs b/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.Designer.cs new file mode 100644 index 000000000..268457e35 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.Designer.cs @@ -0,0 +1,279 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20221123174502_R3_0.3.5")] + partial class R3_035 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.11"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("Workflows") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.cs b/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.cs new file mode 100644 index 000000000..f060d99ff --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20221123174502_R3_0.3.5.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R3_035 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "DicomAssociationHistories", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + DateTimeDisconnected = table.Column(type: "TEXT", nullable: false), + CorrelationId = table.Column(type: "TEXT", nullable: false), + FileCount = table.Column(type: "INTEGER", nullable: false), + CallingAeTitle = table.Column(type: "TEXT", nullable: true), + CalledAeTitle = table.Column(type: "TEXT", nullable: false), + RemoteHost = table.Column(type: "TEXT", nullable: false), + RemotePort = table.Column(type: "INTEGER", nullable: false), + Errors = table.Column(type: "TEXT", nullable: true), + Duration = table.Column(type: "TEXT", nullable: false), + DateTimeCreated = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DicomAssociationHistories", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DicomAssociationHistories"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.Designer.cs b/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.Designer.cs new file mode 100644 index 000000000..a8c56f98d --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.Designer.cs @@ -0,0 +1,313 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20221215010255_R3_0.3.6")] + partial class R3_036 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.12"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.cs b/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.cs new file mode 100644 index 000000000..25e948b29 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20221215010255_R3_0.3.6.cs @@ -0,0 +1,214 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R3_036 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "SourceApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateTimeUpdated", + table: "SourceApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + table: "SourceApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Files", + table: "Payloads", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Workflows", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "IgnoredSopClasses", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "AllowedSopClasses", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "InferenceRequests", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateTimeCreated", + table: "InferenceRequests", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AlterColumn( + name: "Errors", + table: "DicomAssociationHistories", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CallingAeTitle", + table: "DicomAssociationHistories", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "CreatedBy", + table: "DestinationApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateTimeUpdated", + table: "DestinationApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + table: "DestinationApplicationEntities", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "SourceApplicationEntities"); + + migrationBuilder.DropColumn( + name: "DateTimeUpdated", + table: "SourceApplicationEntities"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + table: "SourceApplicationEntities"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "MonaiApplicationEntities"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "InferenceRequests"); + + migrationBuilder.DropColumn( + name: "DateTimeCreated", + table: "InferenceRequests"); + + migrationBuilder.DropColumn( + name: "CreatedBy", + table: "DestinationApplicationEntities"); + + migrationBuilder.DropColumn( + name: "DateTimeUpdated", + table: "DestinationApplicationEntities"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + table: "DestinationApplicationEntities"); + + migrationBuilder.AlterColumn( + name: "Files", + table: "Payloads", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "Workflows", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "IgnoredSopClasses", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "AllowedSopClasses", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "Errors", + table: "DicomAssociationHistories", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "CallingAeTitle", + table: "DicomAssociationHistories", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.Designer.cs b/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.Designer.cs new file mode 100644 index 000000000..2baa6fcd4 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.Designer.cs @@ -0,0 +1,316 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20230131233123_R3_0.3.8")] + partial class R3_038 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.13"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.cs b/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.cs new file mode 100644 index 000000000..5b56367b8 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230131233123_R3_0.3.8.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R3_038 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MachineName", + table: "Payloads", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "MachineName", + table: "Payloads"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.Designer.cs b/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.Designer.cs new file mode 100644 index 000000000..15035eb70 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.Designer.cs @@ -0,0 +1,322 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20230327190827_R3_0.3.15")] + partial class R3_0315 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.15"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.cs b/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.cs new file mode 100644 index 000000000..a190f7719 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230327190827_R3_0.3.15.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R3_0315 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateTimeUpdated", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpdatedBy", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateTimeUpdated", + table: "MonaiApplicationEntities"); + + migrationBuilder.DropColumn( + name: "UpdatedBy", + table: "MonaiApplicationEntities"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.Designer.cs b/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.Designer.cs new file mode 100644 index 000000000..954d66342 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.Designer.cs @@ -0,0 +1,378 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20230824185313_R4_0.4.0")] + partial class R4_040 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.21"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.cs b/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.cs new file mode 100644 index 000000000..d1fec3502 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230824185313_R4_0.4.0.cs @@ -0,0 +1,95 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R4_040 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DataOrigins", + table: "Payloads", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "DataTrigger", + table: "Payloads", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "TaskId", + table: "Payloads", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "WorkflowInstanceId", + table: "Payloads", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "PlugInAssemblies", + table: "MonaiApplicationEntities", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateTable( + name: "VirtualApplicationEntities", + columns: table => new + { + Name = table.Column(type: "TEXT", nullable: false), + VirtualAeTitle = table.Column(type: "TEXT", nullable: false), + Workflows = table.Column(type: "TEXT", nullable: false), + PlugInAssemblies = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true), + DateTimeUpdated = table.Column(type: "TEXT", nullable: true), + DateTimeCreated = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_VirtualApplicationEntities", x => x.Name); + }); + + migrationBuilder.CreateIndex( + name: "idx_virtualae_name", + table: "VirtualApplicationEntities", + column: "Name", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "VirtualApplicationEntities"); + + migrationBuilder.DropColumn( + name: "DataOrigins", + table: "Payloads"); + + migrationBuilder.DropColumn( + name: "DataTrigger", + table: "Payloads"); + + migrationBuilder.DropColumn( + name: "TaskId", + table: "Payloads"); + + migrationBuilder.DropColumn( + name: "WorkflowInstanceId", + table: "Payloads"); + + migrationBuilder.DropColumn( + name: "PlugInAssemblies", + table: "MonaiApplicationEntities"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.Designer.cs b/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.Designer.cs new file mode 100644 index 000000000..35b9137a5 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.Designer.cs @@ -0,0 +1,382 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20230907182457_R4_0.4.1")] + partial class R4_041 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.21"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.cs b/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.cs new file mode 100644 index 000000000..0b5ddaf9a --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20230907182457_R4_0.4.1.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class R4_041 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PayloadIds", + table: "DicomAssociationHistories", + type: "TEXT", + nullable: false, + defaultValue: ""); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PayloadIds", + table: "DicomAssociationHistories"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231120161347_202311201611.Designer.cs b/src/Database/EntityFramework/Migrations/20231120161347_202311201611.Designer.cs new file mode 100755 index 000000000..cecea913d --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231120161347_202311201611.Designer.cs @@ -0,0 +1,431 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20231120161347_202311201611")] + partial class _202311201611 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.22"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231120161347_202311201611.cs b/src/Database/EntityFramework/Migrations/20231120161347_202311201611.cs new file mode 100755 index 000000000..d46799cd3 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231120161347_202311201611.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class _202311201611 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DestinationFolder", + table: "Payloads", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateTable( + name: "ExternalAppDetails", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + StudyInstanceUid = table.Column(type: "TEXT", nullable: false), + StudyInstanceUidOutBound = table.Column(type: "TEXT", nullable: false), + WorkflowInstanceId = table.Column(type: "TEXT", nullable: false), + ExportTaskID = table.Column(type: "TEXT", nullable: false), + CorrelationId = table.Column(type: "TEXT", nullable: false), + DestinationFolder = table.Column(type: "TEXT", nullable: true), + PatientId = table.Column(type: "TEXT", nullable: false), + PatientIdOutBound = table.Column(type: "TEXT", nullable: false), + DateTimeCreated = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExternalAppDetails", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ExternalAppDetails"); + + migrationBuilder.DropColumn( + name: "DestinationFolder", + table: "Payloads"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.Designer.cs b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.Designer.cs new file mode 100755 index 000000000..742aec068 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.Designer.cs @@ -0,0 +1,505 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20231204113501_Hl7DEstinationAndConfig")] + partial class Hl7DEstinationAndConfig + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.25"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.HL7DestinationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique() + .HasDatabaseName("idx_destination_name1"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.ToTable("HL7DestinationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all2"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs new file mode 100755 index 000000000..988279f07 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231204113501_Hl7DEstinationAndConfig.cs @@ -0,0 +1,80 @@ + +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class Hl7DEstinationAndConfig : Migration + { + private static readonly string[] HL7DestinationEntitiesColumns = ["Name", "AeTitle", "HostIp", "Port"]; + + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Hl7ApplicationConfig", + columns: table => new + { + Name = table.Column(type: "TEXT", nullable: false), + SendingId = table.Column(type: "TEXT", nullable: false), + DataLink = table.Column(type: "TEXT", nullable: false), + DataMapping = table.Column(type: "TEXT", nullable: false), + PlugInAssemblies = table.Column(type: "TEXT", nullable: false), + DateTimeCreated = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Hl7ApplicationConfig", x => x.Name); + }); + + migrationBuilder.CreateTable( + name: "HL7DestinationEntities", + columns: table => new + { + Name = table.Column(type: "TEXT", nullable: false), + Port = table.Column(type: "INTEGER", nullable: false), + DateTimeCreated = table.Column(type: "TEXT", nullable: false), + AeTitle = table.Column(type: "TEXT", nullable: false), + HostIp = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", nullable: true), + UpdatedBy = table.Column(type: "TEXT", nullable: true), + DateTimeUpdated = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HL7DestinationEntities", x => x.Name); + }); + + migrationBuilder.CreateIndex( + name: "idx_hl7_name", + table: "Hl7ApplicationConfig", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "idx_destination_name1", + table: "HL7DestinationEntities", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities", + columns: HL7DestinationEntitiesColumns, + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Hl7ApplicationConfig"); + + migrationBuilder.DropTable( + name: "HL7DestinationEntities"); + + migrationBuilder.DropIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs new file mode 100755 index 000000000..256eb5527 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.Designer.cs @@ -0,0 +1,508 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20231207154732_Hl7Plugins")] + partial class Hl7Plugins + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.25"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.HL7DestinationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique() + .HasDatabaseName("idx_destination_name1"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.ToTable("HL7DestinationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT"); + + b.Property("InputResources") + .HasColumnType("TEXT"); + + b.Property("OutputResources") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TryCount") + .HasColumnType("INTEGER"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all2"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT"); + + b.Property("Identity") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs new file mode 100755 index 000000000..c86631267 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20231207154732_Hl7Plugins.cs @@ -0,0 +1,27 @@ + +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + public partial class Hl7Plugins : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastModified", + table: "Hl7ApplicationConfig", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastModified", + table: "Hl7ApplicationConfig"); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.Designer.cs b/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.Designer.cs new file mode 100755 index 000000000..175ca28e4 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.Designer.cs @@ -0,0 +1,514 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + [DbContext(typeof(InformaticsGatewayContext))] + [Migration("20240118154616_MinorModelChanges")] + partial class MinorModelChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique(); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp", "Port" }, "idx_source_all") + .IsUnique(); + + b.ToTable("DestinationApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.HL7DestinationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique() + .HasDatabaseName("idx_destination_name1"); + + b.HasIndex(new[] { "Name", "HostIp", "Port" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.ToTable("HL7DestinationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("Grouping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_monaiae_name") + .IsUnique(); + + b.ToTable("MonaiApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Rest.InferenceRequest", b => + { + b.Property("InferenceRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("InputMetadata") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "inputMetadata"); + + b.Property("InputResources") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "inputResources"); + + b.Property("OutputResources") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "outputResources"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "priority"); + + b.Property("State") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "state"); + + b.Property("Status") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "status"); + + b.Property("TransactionId") + .IsRequired() + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "transactionID"); + + b.Property("TryCount") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "tryCount"); + + b.HasKey("InferenceRequestId"); + + b.HasIndex(new[] { "InferenceRequestId" }, "idx_inferencerequest_inferencerequestid") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_inferencerequest_state"); + + b.HasIndex(new[] { "TransactionId" }, "idx_inferencerequest_transactionid") + .IsUnique(); + + b.ToTable("InferenceRequests"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.SourceApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("AeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all2"); + + b.HasIndex(new[] { "Name" }, "idx_source_name") + .IsUnique(); + + b.ToTable("SourceApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Storage.Payload", b => + { + b.Property("PayloadId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("Files") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MachineName") + .HasColumnType("TEXT"); + + b.Property("RetryCount") + .HasColumnType("INTEGER"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Timeout") + .HasColumnType("INTEGER"); + + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + + b.HasKey("PayloadId"); + + b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") + .IsUnique(); + + b.HasIndex(new[] { "State" }, "idx_payload_state"); + + b.ToTable("Payloads"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => + { + b.Property("CorrelationId") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "correlationId"); + + b.Property("Identity") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "identity"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("IsUploaded") + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "isUploaded"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "typeName"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "value"); + + b.HasKey("CorrelationId", "Identity"); + + b.HasIndex(new[] { "CorrelationId" }, "idx_storagemetadata_correlation"); + + b.HasIndex(new[] { "CorrelationId", "Identity" }, "idx_storagemetadata_ids"); + + b.HasIndex(new[] { "IsUploaded" }, "idx_storagemetadata_uploaded"); + + b.ToTable("StorageMetadataWrapperEntities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.cs b/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.cs new file mode 100755 index 000000000..87b516469 --- /dev/null +++ b/src/Database/EntityFramework/Migrations/20240118154616_MinorModelChanges.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.Database.Migrations +{ + /// + public partial class MinorModelChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities"); + + migrationBuilder.DropColumn( + name: "DestinationFolder", + table: "Payloads"); + + migrationBuilder.DropColumn( + name: "AeTitle", + table: "HL7DestinationEntities"); + + migrationBuilder.CreateIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities", + columns: NewColumns, + unique: true); + } + + private static readonly string[] OldColumns = ["Name", "AeTitle", "HostIp", "Port"]; + private static readonly string[] NewColumns = ["Name", "HostIp", "Port"]; + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities"); + + migrationBuilder.AddColumn( + name: "DestinationFolder", + table: "Payloads", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "AeTitle", + table: "HL7DestinationEntities", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.CreateIndex( + name: "idx_source_all_HL7Destination", + table: "HL7DestinationEntities", + columns: OldColumns, + unique: true); + } + } +} diff --git a/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs b/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs old mode 100644 new mode 100755 index d25aff85d..ba8662f16 --- a/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs +++ b/src/Database/EntityFramework/Migrations/InformaticsGatewayContextModelSnapshot.cs @@ -15,9 +15,94 @@ partial class InformaticsGatewayContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "6.0.10"); + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); - modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DestinationApplicationEntity", b => + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.DataKeyValuePair", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Key"); + + b.ToTable("DataKeyValuePair"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Hl7ApplicationConfigEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("DataLink") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataMapping") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SendingId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_hl7_name") + .IsUnique(); + + b.ToTable("Hl7ApplicationConfig"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DestinationApplicationEntity", b => { b.Property("Name") .HasColumnType("TEXT"); @@ -26,9 +111,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("CreatedBy") + .HasColumnType("TEXT"); + b.Property("DateTimeCreated") .HasColumnType("TEXT"); + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + b.Property("HostIp") .IsRequired() .HasColumnType("TEXT"); @@ -36,6 +127,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Port") .HasColumnType("INTEGER"); + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + b.HasKey("Name"); b.HasIndex(new[] { "Name" }, "idx_destination_name") @@ -47,7 +141,139 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DestinationApplicationEntities"); }); - modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.MonaiApplicationEntity", b => + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.DicomAssociationInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalledAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CallingAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeDisconnected") + .HasColumnType("TEXT"); + + b.Property("Duration") + .HasColumnType("TEXT"); + + b.Property("Errors") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileCount") + .HasColumnType("INTEGER"); + + b.Property("PayloadIds") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemoteHost") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RemotePort") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("DicomAssociationHistories"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.ExternalAppDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DestinationFolder") + .HasColumnType("TEXT"); + + b.Property("ExportTaskID") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PatientIdOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUidOutBound") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ExternalAppDetails"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.HL7DestinationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("HostIp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Port") + .HasColumnType("INTEGER"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_destination_name") + .IsUnique() + .HasDatabaseName("idx_destination_name1"); + + b.HasIndex(new[] { "Name", "HostIp", "Port" }, "idx_source_all") + .IsUnique() + .HasDatabaseName("idx_source_all1"); + + b.ToTable("HL7DestinationEntities"); + }); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.Models.MonaiApplicationEntity", b => { b.Property("Name") .HasColumnType("TEXT") @@ -58,22 +284,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("AllowedSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedBy") .HasColumnType("TEXT"); b.Property("DateTimeCreated") .HasColumnType("TEXT"); + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + b.Property("Grouping") .IsRequired() .HasColumnType("TEXT"); b.Property("IgnoredSopClasses") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() .HasColumnType("TEXT"); b.Property("Timeout") .HasColumnType("INTEGER"); + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + b.Property("Workflows") + .IsRequired() .HasColumnType("TEXT"); b.HasKey("Name"); @@ -90,30 +332,45 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("TEXT"); - b.Property("InputMetadata") + b.Property("CreatedBy") .HasColumnType("TEXT"); - b.Property("InputResources") + b.Property("DateTimeCreated") + .IsRequired() .HasColumnType("TEXT"); + b.Property("InputMetadata") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "inputMetadata"); + + b.Property("InputResources") + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "inputResources"); + b.Property("OutputResources") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "outputResources"); b.Property("Priority") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "priority"); b.Property("State") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "state"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "status"); b.Property("TransactionId") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "transactionID"); b.Property("TryCount") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "tryCount"); b.HasKey("InferenceRequestId"); @@ -137,18 +394,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("CreatedBy") + .HasColumnType("TEXT"); + b.Property("DateTimeCreated") .HasColumnType("TEXT"); + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + b.Property("HostIp") .IsRequired() .HasColumnType("TEXT"); + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + b.HasKey("Name"); b.HasIndex(new[] { "Name", "AeTitle", "HostIp" }, "idx_source_all") .IsUnique() - .HasDatabaseName("idx_source_all1"); + .HasDatabaseName("idx_source_all2"); b.HasIndex(new[] { "Name" }, "idx_source_name") .IsUnique(); @@ -166,25 +432,43 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); + b.Property("DataOrigins") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DataTrigger") + .IsRequired() + .HasColumnType("TEXT"); + b.Property("DateTimeCreated") .HasColumnType("TEXT"); b.Property("Files") + .IsRequired() .HasColumnType("TEXT"); b.Property("Key") .IsRequired() .HasColumnType("TEXT"); + b.Property("MachineName") + .HasColumnType("TEXT"); + b.Property("RetryCount") .HasColumnType("INTEGER"); b.Property("State") .HasColumnType("INTEGER"); + b.Property("TaskId") + .HasColumnType("TEXT"); + b.Property("Timeout") .HasColumnType("INTEGER"); + b.Property("WorkflowInstanceId") + .HasColumnType("TEXT"); + b.HasKey("PayloadId"); b.HasIndex(new[] { "CorrelationId", "PayloadId" }, "idx_payload_ids") @@ -195,27 +479,70 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Payloads"); }); + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Api.VirtualApplicationEntity", b => + { + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnOrder(0); + + b.Property("CreatedBy") + .HasColumnType("TEXT"); + + b.Property("DateTimeCreated") + .HasColumnType("TEXT"); + + b.Property("DateTimeUpdated") + .HasColumnType("TEXT"); + + b.Property("PlugInAssemblies") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UpdatedBy") + .HasColumnType("TEXT"); + + b.Property("VirtualAeTitle") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Workflows") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Name"); + + b.HasIndex(new[] { "Name" }, "idx_virtualae_name") + .IsUnique(); + + b.ToTable("VirtualApplicationEntities"); + }); + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.Database.Api.StorageMetadataWrapper", b => { b.Property("CorrelationId") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "correlationId"); b.Property("Identity") - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "identity"); b.Property("DateTimeCreated") .HasColumnType("TEXT"); b.Property("IsUploaded") - .HasColumnType("INTEGER"); + .HasColumnType("INTEGER") + .HasAnnotation("Relational:JsonPropertyName", "isUploaded"); b.Property("TypeName") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "typeName"); b.Property("Value") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("TEXT") + .HasAnnotation("Relational:JsonPropertyName", "value"); b.HasKey("CorrelationId", "Identity"); diff --git a/src/Database/EntityFramework/Monai.Deploy.InformaticsGateway.Database.EntityFramework.csproj b/src/Database/EntityFramework/Monai.Deploy.InformaticsGateway.Database.EntityFramework.csproj old mode 100644 new mode 100755 index ae3bd804e..5bcc827c0 --- a/src/Database/EntityFramework/Monai.Deploy.InformaticsGateway.Database.EntityFramework.csproj +++ b/src/Database/EntityFramework/Monai.Deploy.InformaticsGateway.Database.EntityFramework.csproj @@ -1,4 +1,4 @@ - - - Monai.Deploy.InformaticsGateway.Database.EntityFramework - net6.0 + net8.0 Apache-2.0 enable ..\..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset enable true + false - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - + + + + + + + + + - - + \ No newline at end of file diff --git a/src/Database/EntityFramework/Repositories/DestinationApplicationEntityRepository.cs b/src/Database/EntityFramework/Repositories/DestinationApplicationEntityRepository.cs old mode 100644 new mode 100755 index 182ccee0e..4a3f1cdc2 --- a/src/Database/EntityFramework/Repositories/DestinationApplicationEntityRepository.cs +++ b/src/Database/EntityFramework/Repositories/DestinationApplicationEntityRepository.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; @@ -41,24 +41,24 @@ public class DestinationApplicationEntityRepository : IDestinationApplicationEnt public DestinationApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) + IOptions options) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } public async Task AddAsync(DestinationApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -79,7 +79,7 @@ public async Task ContainsAsync(Expression FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -89,7 +89,7 @@ public async Task ContainsAsync(Expression RemoveAsync(DestinationApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -109,7 +109,7 @@ public async Task> ToListAsync(CancellationTo public async Task UpdateAsync(DestinationApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/EntityFramework/Repositories/DicomAssociationInfoRepository.cs b/src/Database/EntityFramework/Repositories/DicomAssociationInfoRepository.cs new file mode 100755 index 000000000..2e000e947 --- /dev/null +++ b/src/Database/EntityFramework/Repositories/DicomAssociationInfoRepository.cs @@ -0,0 +1,117 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories +{ + public class DicomAssociationInfoRepository : IDicomAssociationInfoRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly InformaticsGatewayContext _informaticsGatewayContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + private bool _disposedValue; + + public DicomAssociationInfoRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _informaticsGatewayContext.Set(); + } + + public async Task AddAsync(DicomAssociationInfo item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.AddAsync(item, cancellationToken).ConfigureAwait(false); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task> GetAllAsync(int skip, + int? limit, + DateTime startTime, + DateTime endTime, + CancellationToken cancellationToken) + { + return await _dataset + .Where(t => + t.DateTimeDisconnected >= startTime.ToUniversalTime() && + t.DateTimeDisconnected <= endTime.ToUniversalTime()) + .Skip(skip) + .Take(limit!.Value) + .ToListAsync(cancellationToken) + .ConfigureAwait(false); + } + + public Task CountAsync() => _dataset.LongCountAsync(); + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _informaticsGatewayContext.Dispose(); + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/EntityFramework/Repositories/ExternalAppDetailsRepository.cs b/src/Database/EntityFramework/Repositories/ExternalAppDetailsRepository.cs new file mode 100755 index 000000000..52fce4a50 --- /dev/null +++ b/src/Database/EntityFramework/Repositories/ExternalAppDetailsRepository.cs @@ -0,0 +1,110 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Polly.Retry; +using Microsoft.Extensions.Logging; +using Polly; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories +{ + public class ExternalAppDetailsRepository : IExternalAppDetailsRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly InformaticsGatewayContext _informaticsGatewayContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + private bool _disposedValue; + + public ExternalAppDetailsRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _informaticsGatewayContext.Set(); + } + + public async Task AddAsync(ExternalAppDetails details, CancellationToken cancellationToken = default) + { + Guard.Against.Null(details, nameof(details)); + + await _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.AddAsync(details, cancellationToken).ConfigureAwait(false); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task> GetAsync(string studyInstanceId, CancellationToken cancellationToken = default) + { + return await _dataset + .Where(t => t.StudyInstanceUid == studyInstanceId).ToListAsync(cancellationToken) + .ConfigureAwait(false); + } + + public async Task GetByPatientIdOutboundAsync(string patientId, CancellationToken cancellationToken) + { + return await _dataset + .FirstOrDefaultAsync(t => t.PatientIdOutBound == patientId, cancellationToken) + .ConfigureAwait(false); + } + + public async Task GetByStudyIdOutboundAsync(string studyInstanceId, CancellationToken cancellationToken) + { + return await _dataset + .FirstOrDefaultAsync(t => t.StudyInstanceUidOutBound == studyInstanceId, cancellationToken) + .ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _informaticsGatewayContext.Dispose(); + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/EntityFramework/Repositories/HL7DestinationEntityRepository.cs b/src/Database/EntityFramework/Repositories/HL7DestinationEntityRepository.cs new file mode 100644 index 000000000..2c0ab03ae --- /dev/null +++ b/src/Database/EntityFramework/Repositories/HL7DestinationEntityRepository.cs @@ -0,0 +1,143 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories +{ + public class HL7DestinationEntityRepository : IHL7DestinationEntityRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly InformaticsGatewayContext _informaticsGatewayContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + private bool _disposedValue; + + public HL7DestinationEntityRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _informaticsGatewayContext.Set(); + } + + public async Task AddAsync(HL7DestinationEntity item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.AddAsync(item, cancellationToken).ConfigureAwait(false); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + var func = predicate.Compile(); + return await Task.FromResult(_dataset.Any(func)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.FirstOrDefaultAsync(p => p.Name.Equals(name), cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Remove(entity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task UpdateAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Update(entity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _informaticsGatewayContext.Dispose(); + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs b/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs new file mode 100755 index 000000000..898181503 --- /dev/null +++ b/src/Database/EntityFramework/Repositories/Hl7ApplicationConfigRepository.cs @@ -0,0 +1,95 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories +{ + public class Hl7ApplicationConfigRepository : IHl7ApplicationConfigRepository + { + private readonly ILogger _logger; + private readonly InformaticsGatewayContext _informaticsGatewayContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + + public Hl7ApplicationConfigRepository(ILogger logger, + IOptions options, IServiceScopeFactory serviceScopeFactory) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + var scope = serviceScopeFactory.CreateScope(); + + _informaticsGatewayContext = scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _informaticsGatewayContext.Set(); + } + + public Task> GetAllAsync(CancellationToken cancellationToken = default) => + _retryPolicy.ExecuteAsync(() => _dataset.ToListAsync(cancellationToken)); + + public Task GetByIdAsync(string id) => + _retryPolicy.ExecuteAsync(() => _dataset.FirstOrDefaultAsync(x => x.Id.Equals(id))); + + public Task DeleteAsync(string id, CancellationToken cancellationToken) + { + return _retryPolicy.ExecuteAsync(async () => + { + var entity = await GetByIdAsync(id).ConfigureAwait(false) ?? throw new DatabaseException("Failed to delete entity."); + var result = _dataset.Remove(entity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }); + } + + public Task CreateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default) + { + return _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.AddAsync(configEntity, cancellationToken).ConfigureAwait(false); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }); + } + + public Task UpdateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default) + { + return _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Update(configEntity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + })!; + } + } +} diff --git a/src/Database/EntityFramework/Repositories/InferenceRequestRepository.cs b/src/Database/EntityFramework/Repositories/InferenceRequestRepository.cs old mode 100644 new mode 100755 index d3780c8aa..035a1d0e8 --- a/src/Database/EntityFramework/Repositories/InferenceRequestRepository.cs +++ b/src/Database/EntityFramework/Repositories/InferenceRequestRepository.cs @@ -33,7 +33,6 @@ namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories public class InferenceRequestRepository : InferenceRequestRepositoryBase, IDisposable { private readonly ILogger _logger; - private readonly IOptions _options; private readonly IServiceScope _scope; private readonly InformaticsGatewayContext _informaticsGatewayContext; private readonly AsyncRetryPolicy _retryPolicy; @@ -45,23 +44,22 @@ public class InferenceRequestRepository : InferenceRequestRepositoryBase, IDispo public InferenceRequestRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) : base(logger, options) + IOptions options) : base(logger, options) { - Guard.Against.Null(serviceScopeFactory); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _options = options ?? throw new ArgumentNullException(nameof(options)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } public override async Task AddAsync(InferenceRequest inferenceRequest, CancellationToken cancellationToken = default) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "TransactionId", inferenceRequest.TransactionId } }); await _retryPolicy.ExecuteAsync(async () => @@ -103,7 +101,7 @@ public override async Task TakeAsync(CancellationToken cancell public override async Task GetInferenceRequestAsync(string transactionId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(transactionId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -113,7 +111,7 @@ public override async Task TakeAsync(CancellationToken cancell public override async Task GetInferenceRequestAsync(Guid inferenceRequestId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrEmpty(inferenceRequestId); + Guard.Against.NullOrEmpty(inferenceRequestId, nameof(inferenceRequestId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -123,7 +121,7 @@ public override async Task TakeAsync(CancellationToken cancell protected override async Task SaveAsync(InferenceRequest inferenceRequest, CancellationToken cancellationToken = default) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/EntityFramework/Repositories/MonaiApplicationEntityRepository.cs b/src/Database/EntityFramework/Repositories/MonaiApplicationEntityRepository.cs old mode 100644 new mode 100755 index 41148acbc..e8001a728 --- a/src/Database/EntityFramework/Repositories/MonaiApplicationEntityRepository.cs +++ b/src/Database/EntityFramework/Repositories/MonaiApplicationEntityRepository.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; @@ -41,24 +41,24 @@ public class MonaiApplicationEntityRepository : IMonaiApplicationEntityRepositor public MonaiApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) + IOptions options) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } public async Task AddAsync(MonaiApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -79,7 +79,7 @@ public async Task ContainsAsync(Expression FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -89,7 +89,7 @@ public async Task ContainsAsync(Expression RemoveAsync(MonaiApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -109,7 +109,7 @@ public async Task> ToListAsync(CancellationToken ca public async Task UpdateAsync(MonaiApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/EntityFramework/Repositories/PayloadRepository.cs b/src/Database/EntityFramework/Repositories/PayloadRepository.cs old mode 100644 new mode 100755 index 8f849f7f0..0a7eb0cb3 --- a/src/Database/EntityFramework/Repositories/PayloadRepository.cs +++ b/src/Database/EntityFramework/Repositories/PayloadRepository.cs @@ -14,7 +14,6 @@ * limitations under the License. */ -using System.Linq.Expressions; using Ardalis.GuardClauses; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -41,24 +40,24 @@ public class PayloadRepository : IPayloadRepository, IDisposable public PayloadRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) + IOptions options) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } public async Task AddAsync(Payload item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -68,17 +67,9 @@ public async Task AddAsync(Payload item, CancellationToken cancellation }).ConfigureAwait(false); } - public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) - { - return await _retryPolicy.ExecuteAsync(async () => - { - return await _dataset.AnyAsync(predicate, cancellationToken).ConfigureAwait(false); - }).ConfigureAwait(false); - } - public async Task RemoveAsync(Payload entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -98,7 +89,7 @@ public async Task> ToListAsync(CancellationToken cancellationToken public async Task UpdateAsync(Payload entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -113,7 +104,7 @@ public async Task RemovePendingPayloadsAsync(CancellationToken cancellation return await _retryPolicy.ExecuteAsync(async () => { var count = 0; - await _dataset.Where(p => p.State == Payload.PayloadState.Created).ForEachAsync( + await _dataset.Where(p => p.State == Payload.PayloadState.Created && p.MachineName == Environment.MachineName).ForEachAsync( p => { _dataset.Remove(p); diff --git a/src/Database/EntityFramework/Repositories/SourceApplicationEntityRepository.cs b/src/Database/EntityFramework/Repositories/SourceApplicationEntityRepository.cs old mode 100644 new mode 100755 index 0402eca99..c0829f724 --- a/src/Database/EntityFramework/Repositories/SourceApplicationEntityRepository.cs +++ b/src/Database/EntityFramework/Repositories/SourceApplicationEntityRepository.cs @@ -41,24 +41,24 @@ public class SourceApplicationEntityRepository : ISourceApplicationEntityReposit public SourceApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) + IOptions options) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } public async Task AddAsync(SourceApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -79,7 +79,7 @@ public async Task ContainsAsync(Expression FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -87,9 +87,19 @@ public async Task ContainsAsync(Expression FindByAETAsync(string aeTitle, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.Where(p => p.AeTitle.Equals(aeTitle)).ToArrayAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + public async Task RemoveAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -109,7 +119,7 @@ public async Task> ToListAsync(CancellationToken c public async Task UpdateAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/EntityFramework/Repositories/StorageMetadataWrapperRepository.cs b/src/Database/EntityFramework/Repositories/StorageMetadataWrapperRepository.cs old mode 100644 new mode 100755 index adb69a347..096dc04c3 --- a/src/Database/EntityFramework/Repositories/StorageMetadataWrapperRepository.cs +++ b/src/Database/EntityFramework/Repositories/StorageMetadataWrapperRepository.cs @@ -43,24 +43,24 @@ public class StorageMetadataWrapperRepository : StorageMetadataRepositoryBase, I public StorageMetadataWrapperRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) : base(logger) + IOptions options) : base(logger) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); _retryPolicy = Policy.Handle(p => p is not ArgumentException).WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); _dataset = _informaticsGatewayContext.Set(); } protected override async Task AddAsyncInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); await _retryPolicy.ExecuteAsync(async () => { @@ -73,7 +73,7 @@ await _retryPolicy.ExecuteAsync(async () => protected override async Task UpdateInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); await _retryPolicy.ExecuteAsync(async () => { @@ -95,7 +95,7 @@ await _retryPolicy.ExecuteAsync(async () => public override async Task> GetFileStorageMetdadataAsync(string correlationId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -108,8 +108,8 @@ public override async Task> GetFileStorageMetdadataAs public override async Task GetFileStorageMetdadataAsync(string correlationId, string identity, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(identity); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(identity, nameof(identity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -122,7 +122,7 @@ public override async Task> GetFileStorageMetdadataAs protected override async Task DeleteInternalAsync(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/EntityFramework/Repositories/VirtualApplicationEntityRepository.cs b/src/Database/EntityFramework/Repositories/VirtualApplicationEntityRepository.cs new file mode 100755 index 000000000..548a245f3 --- /dev/null +++ b/src/Database/EntityFramework/Repositories/VirtualApplicationEntityRepository.cs @@ -0,0 +1,153 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories +{ + public class VirtualApplicationEntityRepository : IVirtualApplicationEntityRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly InformaticsGatewayContext _informaticsGatewayContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + private bool _disposedValue; + + public VirtualApplicationEntityRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _informaticsGatewayContext = _scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _informaticsGatewayContext.Set(); + } + + public async Task AddAsync(VirtualApplicationEntity item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.AddAsync(item, cancellationToken).ConfigureAwait(false); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + var func = predicate.Compile(); + return await Task.FromResult(_dataset.Any(func)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.FirstOrDefaultAsync(p => p.Name.Equals(name), cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByAeTitleAsync(string aeTitle, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.FirstOrDefaultAsync(p => p.VirtualAeTitle.Equals(aeTitle), cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Remove(entity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task UpdateAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Update(entity); + await _informaticsGatewayContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _informaticsGatewayContext.Dispose(); + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/EntityFramework/Test/DestinationApplicationEntityRepositoryTest.cs b/src/Database/EntityFramework/Test/DestinationApplicationEntityRepositoryTest.cs old mode 100644 new mode 100755 index 483ca3b73..77ad38dd4 --- a/src/Database/EntityFramework/Test/DestinationApplicationEntityRepositoryTest.cs +++ b/src/Database/EntityFramework/Test/DestinationApplicationEntityRepositoryTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; using Moq; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test { @@ -32,7 +32,7 @@ public class DestinationApplicationEntityRepositoryTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock _serviceScope; private readonly IServiceProvider _serviceProvider; @@ -44,7 +44,7 @@ public DestinationApplicationEntityRepositoryTest(SqliteDatabaseFixture database _serviceScopeFactory = new Mock(); _logger = new Mock>(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _serviceScope = new Mock(); var services = new ServiceCollection(); @@ -55,7 +55,7 @@ public DestinationApplicationEntityRepositoryTest(SqliteDatabaseFixture database _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -65,8 +65,8 @@ public async Task GivenADestinationApplicationEntity_WhenAddingToDatabase_Expect var aet = new DestinationApplicationEntity { AeTitle = "AET", HostIp = "1.2.3.4", Port = 114, Name = "AET" }; var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(aet).ConfigureAwait(false); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(false); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(aet.AeTitle, actual!.AeTitle); @@ -80,15 +80,15 @@ public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToRet { var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(false); + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(false); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.AeTitle != "AET1" && p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.AeTitle != "AET1" && p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Port == 999).ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Port == 999).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(result); } @@ -97,14 +97,14 @@ public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturn { var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var actual = await store.FindByNameAsync("AET1").ConfigureAwait(false); + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal("AET1", actual!.AeTitle); Assert.Equal("1.2.3.4", actual!.HostIp); Assert.Equal(114, actual!.Port); Assert.Equal("AET1", actual!.Name); - actual = await store.FindByNameAsync("AET6").ConfigureAwait(false); + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(actual); } @@ -113,13 +113,13 @@ public async Task GivenADestinationApplicationEntity_WhenRemoveIsCalled_ExpectIt { var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET5").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); - var actual = await store.RemoveAsync(expected!).ConfigureAwait(false); + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Same(expected, actual); - var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(false); + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(dbResult); } @@ -128,8 +128,8 @@ public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsC { var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(false); - var actual = await store.ToListAsync().ConfigureAwait(false); + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); } @@ -139,17 +139,17 @@ public async Task GivenADestinationApplicationEntity_WhenUpdatedIsCalled_ExpectI { var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); expected!.AeTitle = "AET100"; expected!.Port = 1000; expected!.HostIp = "loalhost"; - var actual = await store.UpdateAsync(expected).ConfigureAwait(false); + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); - var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(dbResult); Assert.Equal(expected.AeTitle, dbResult!.AeTitle); Assert.Equal(expected.HostIp, dbResult!.HostIp); diff --git a/src/Database/EntityFramework/Test/DicomAssociationInfoRepositoryTest.cs b/src/Database/EntityFramework/Test/DicomAssociationInfoRepositoryTest.cs new file mode 100755 index 000000000..a8c90d367 --- /dev/null +++ b/src/Database/EntityFramework/Test/DicomAssociationInfoRepositoryTest.cs @@ -0,0 +1,121 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [Collection("SqliteDatabase")] + public class DicomAssociationInfoRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public DicomAssociationInfoRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithDicomAssociationInfoEntries(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenGetAllAsyncCalled_ExpectLimitedEntitiesToBeReturned() + { + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var startTime = DateTime.Now; + var endTime = DateTime.MinValue; + var filter = new Func(t => + t.DateTimeDisconnected >= startTime.ToUniversalTime() && + t.DateTimeDisconnected <= endTime.ToUniversalTime()); + + var expected = _databaseFixture.DatabaseContext.Set() + .Where(filter) + .Skip(0) + .Take(1) + .ToList(); + var actual = await store.GetAllAsync(0, 1, startTime, endTime, default).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenADicomAssociationInfo_WhenAddingToDatabase_ExpectItToBeSaved() + { + var association = new DicomAssociationInfo { CalledAeTitle = "called", CallingAeTitle = "calling", CorrelationId = Guid.NewGuid().ToString(), DateTimeCreated = DateTime.UtcNow, RemoteHost = "host", RemotePort = 100 }; + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(null); + association.FileReceived(string.Empty); + association.Disconnect(); + association.Disconnect(); + + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(association).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Id.Equals(association.Id)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(association.DateTimeCreated, actual!.DateTimeCreated); + Assert.Equal(association.DateTimeDisconnected, actual!.DateTimeDisconnected); + Assert.Equal(3, actual!.FileCount); + Assert.Equal(association.FileCount, actual!.FileCount); + Assert.Equal(association.Duration, actual!.Duration); + Assert.Equal(association.CalledAeTitle, actual!.CalledAeTitle); + Assert.Equal(association.CallingAeTitle, actual!.CallingAeTitle); + Assert.Equal(association.CorrelationId, actual!.CorrelationId); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Equal(expected, actual); + } + } +} diff --git a/src/Database/EntityFramework/Test/ExternalAppDetailsRepositoryTest.cs b/src/Database/EntityFramework/Test/ExternalAppDetailsRepositoryTest.cs new file mode 100755 index 000000000..b051f17b0 --- /dev/null +++ b/src/Database/EntityFramework/Test/ExternalAppDetailsRepositoryTest.cs @@ -0,0 +1,116 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Moq; + + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [Collection("SqliteDatabase")] + public class ExternalAppDetailsRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public ExternalAppDetailsRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithExternalAppDetailsEntries(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + [Fact] + public async Task GivenDestinationExternalAppInTheDatabase_WhenGetAsyncCalled_ExpectEntitieToBeReturned() + { + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var startTime = DateTime.Now; + var endTime = DateTime.MinValue; + + var expected = _databaseFixture.DatabaseContext.Set() + .Where(t => t.StudyInstanceUid == "1"); + var actual = await store.GetAsync("1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenDestinationExternalAppInTheDatabase_WhenGetAsyncCalled_ExpectEntitieToBeReturned2() + { + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var startTime = DateTime.Now; + var endTime = DateTime.MinValue; + + var expected = _databaseFixture.DatabaseContext.Set() + .Where(t => t.PatientIdOutBound == "2") + .Take(1).First(); + var actual = await store.GetByPatientIdOutboundAsync("2", new CancellationToken()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenDestinationExternalAppInTheDatabase_WhenAddingToDatabase_ExpectItToBeSaved() + { + var association = new ExternalAppDetails + { + StudyInstanceUid = "3", + WorkflowInstanceId = "calling", + CorrelationId = Guid.NewGuid().ToString(), + DateTimeCreated = DateTime.UtcNow, + ExportTaskID = "host" + }; + + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(association).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.StudyInstanceUid.Equals(association.StudyInstanceUid)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(association.DateTimeCreated, actual!.DateTimeCreated); + Assert.Equal(association.WorkflowInstanceId, actual!.WorkflowInstanceId); + Assert.Equal(association.ExportTaskID, actual!.ExportTaskID); + Assert.Equal(association.CorrelationId, actual!.CorrelationId); + } + } +} diff --git a/src/Database/EntityFramework/Test/HL7DestinationEntityRepositoryTest.cs b/src/Database/EntityFramework/Test/HL7DestinationEntityRepositoryTest.cs new file mode 100755 index 000000000..9a862e0ec --- /dev/null +++ b/src/Database/EntityFramework/Test/HL7DestinationEntityRepositoryTest.cs @@ -0,0 +1,150 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [Collection("SqliteDatabase")] + public class HL7DestinationEntityRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public HL7DestinationEntityRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithHL7DestinationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = [1, 1, 1]; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET" }; + + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.HostIp, actual!.HostIp); + Assert.Equal(aet.Port, actual!.Port); + Assert.Equal(aet.Name, actual!.Name); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 999).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("1.2.3.4", actual!.HostIp); + Assert.Equal(114, actual!.Port); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenHL7DestinationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.Port = 1000; + expected!.HostIp = "loalhost"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.HostIp, dbResult!.HostIp); + Assert.Equal(expected.Port, dbResult!.Port); + } + } +} diff --git a/src/Database/EntityFramework/Test/InMemoryDatabaseFixture.cs b/src/Database/EntityFramework/Test/InMemoryDatabaseFixture.cs old mode 100644 new mode 100755 index 2aea0684a..01f8d8457 --- a/src/Database/EntityFramework/Test/InMemoryDatabaseFixture.cs +++ b/src/Database/EntityFramework/Test/InMemoryDatabaseFixture.cs @@ -16,6 +16,7 @@ using Microsoft.EntityFrameworkCore; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test { diff --git a/src/Database/EntityFramework/Test/InferenceRequestRepositoryTest.cs b/src/Database/EntityFramework/Test/InferenceRequestRepositoryTest.cs old mode 100644 new mode 100755 index a90ff02d1..154c0b9c4 --- a/src/Database/EntityFramework/Test/InferenceRequestRepositoryTest.cs +++ b/src/Database/EntityFramework/Test/InferenceRequestRepositoryTest.cs @@ -32,7 +32,7 @@ public class InferenceRequestRepositoryTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock _serviceScope; private readonly IServiceProvider _serviceProvider; @@ -43,7 +43,7 @@ public InferenceRequestRepositoryTest(SqliteDatabaseFixture databaseFixture) _serviceScopeFactory = new Mock(); _logger = new Mock>(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _serviceScope = new Mock(); var services = new ServiceCollection(); @@ -54,7 +54,7 @@ public InferenceRequestRepositoryTest(SqliteDatabaseFixture databaseFixture) _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -64,8 +64,8 @@ public async Task GivenAnInferenceRequest_WhenAddingToDatabase_ExpectItToBeSaved var inferenceRequest = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.InferenceRequestId.Equals(inferenceRequest.InferenceRequestId)).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.InferenceRequestId.Equals(inferenceRequest.InferenceRequestId)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(inferenceRequest.InferenceRequestId, actual!.InferenceRequestId); @@ -85,10 +85,10 @@ public async Task GivenAFailedInferenceRequstThatExceededRetries_WhenUpdateIsCal }; var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); - await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(false); + var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(InferenceRequestState.Completed, result!.State); Assert.Equal(InferenceRequestStatus.Fail, result!.Status); @@ -104,10 +104,10 @@ public async Task GivenAFailedInferenceRequst_WhenUpdateIsCalled_ShallRetryLater }; var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); - await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(false); + var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(InferenceRequestState.Queued, result!.State); Assert.Equal(InferenceRequestStatus.Unknown, result!.Status); @@ -124,10 +124,10 @@ public async Task GivenASuccessfulInferenceRequest_WhenUpdateIsCalled_ShallMarkA var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); - await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Success).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Success).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(false); + var result = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.TransactionId == inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(InferenceRequestState.Completed, result!.State); Assert.Equal(InferenceRequestStatus.Success, result!.Status); @@ -138,17 +138,17 @@ public async Task GivenAQueuedInferenceRequests_WhenTakeIsCalled_ShallReturnFirs { var set = _databaseFixture.DatabaseContext.Set(); set.RemoveRange(set.ToList()); - await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(false); + await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); var inferenceRequestInProcess = CreateInferenceRequest(InferenceRequestState.InProcess); var inferenceRequestCompleted = CreateInferenceRequest(InferenceRequestState.Completed); var inferenceRequestQueued = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(false); - await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(false); - await store.AddAsync(inferenceRequestQueued).ConfigureAwait(false); + await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestQueued).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var actual = await store.TakeAsync().ConfigureAwait(false); + var actual = await store.TakeAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(inferenceRequestQueued.InferenceRequestId, actual!.InferenceRequestId); Assert.Equal(InferenceRequestState.InProcess, actual!.State); @@ -167,11 +167,11 @@ public async Task GivenNoQueuedInferenceRequests_WhenTakeIsCalled_ShallReturnNot var inferenceRequestCompleted = CreateInferenceRequest(InferenceRequestState.Completed); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(false); - await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(false); + await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); cancellationTokenSource.CancelAfter(500); - await Assert.ThrowsAsync(async () => await store.TakeAsync(cancellationTokenSource.Token).ConfigureAwait(false)).ConfigureAwait(false); + await Assert.ThrowsAsync(async () => await store.TakeAsync(cancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); } [Fact] @@ -182,27 +182,27 @@ public async Task GivenInferenceRequests_WhenGetInferenceRequestIsCalled_ShallRe var inferenceRequest3 = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest1).ConfigureAwait(false); - await store.AddAsync(inferenceRequest2).ConfigureAwait(false); - await store.AddAsync(inferenceRequest3).ConfigureAwait(false); + await store.AddAsync(inferenceRequest1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequest2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequest3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await store.GetInferenceRequestAsync(inferenceRequest1.TransactionId).ConfigureAwait(false); + var result = await store.GetInferenceRequestAsync(inferenceRequest1.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest1.TransactionId, result!.TransactionId); - result = await store.GetInferenceRequestAsync(inferenceRequest2.TransactionId).ConfigureAwait(false); + result = await store.GetInferenceRequestAsync(inferenceRequest2.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest2.TransactionId, result!.TransactionId); - result = await store.GetInferenceRequestAsync(inferenceRequest3.TransactionId).ConfigureAwait(false); + result = await store.GetInferenceRequestAsync(inferenceRequest3.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest3.TransactionId, result!.TransactionId); - result = await store.GetInferenceRequestAsync(inferenceRequest1.InferenceRequestId).ConfigureAwait(false); + result = await store.GetInferenceRequestAsync(inferenceRequest1.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest1.TransactionId, result!.TransactionId); - result = await store.GetInferenceRequestAsync(inferenceRequest2.InferenceRequestId).ConfigureAwait(false); + result = await store.GetInferenceRequestAsync(inferenceRequest2.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest2.TransactionId, result!.TransactionId); - result = await store.GetInferenceRequestAsync(inferenceRequest3.InferenceRequestId).ConfigureAwait(false); + result = await store.GetInferenceRequestAsync(inferenceRequest3.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest3.TransactionId, result!.TransactionId); } @@ -213,12 +213,12 @@ public async Task GivenInferenceRequests_WhenExistsCalled_ShallReturnCorrectValu var inferenceRequest = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await store.ExistsAsync(inferenceRequest.TransactionId).ConfigureAwait(false); + var result = await store.ExistsAsync(inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ExistsAsync("random").ConfigureAwait(false); + result = await store.ExistsAsync("random").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(result); } @@ -228,9 +228,9 @@ public async Task GivenAMatchingInferenceRequest_WhenGetStatusCalled_ShallReturn var inferenceRequest = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await store.GetStatusAsync(inferenceRequest.TransactionId).ConfigureAwait(false); + var result = await store.GetStatusAsync(inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(result); Assert.Equal(inferenceRequest.TransactionId, result!.TransactionId); @@ -242,9 +242,9 @@ public async Task GivenNoMatchingInferenceRequest_WhenGetStatusCalled_ShallRetur var inferenceRequest = CreateInferenceRequest(); var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(inferenceRequest).ConfigureAwait(false); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await store.GetStatusAsync("bogus").ConfigureAwait(false); + var result = await store.GetStatusAsync("bogus").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(result); } diff --git a/src/Database/EntityFramework/Test/Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test.csproj b/src/Database/EntityFramework/Test/Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test.csproj old mode 100644 new mode 100755 index effb31ad9..c1cf9d767 --- a/src/Database/EntityFramework/Test/Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test.csproj +++ b/src/Database/EntityFramework/Test/Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 enable enable false true - - - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all + - - - + \ No newline at end of file diff --git a/src/Database/EntityFramework/Test/MonaiApplicationEntityRepositoryTest.cs b/src/Database/EntityFramework/Test/MonaiApplicationEntityRepositoryTest.cs old mode 100644 new mode 100755 index 372484845..02f518a58 --- a/src/Database/EntityFramework/Test/MonaiApplicationEntityRepositoryTest.cs +++ b/src/Database/EntityFramework/Test/MonaiApplicationEntityRepositoryTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; using Moq; @@ -32,7 +32,7 @@ public class MonaiApplicationEntityRepositoryTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock _serviceScope; private readonly IServiceProvider _serviceProvider; @@ -44,7 +44,7 @@ public MonaiApplicationEntityRepositoryTest(SqliteDatabaseFixture databaseFixtur _serviceScopeFactory = new Mock(); _logger = new Mock>(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _serviceScope = new Mock(); var services = new ServiceCollection(); @@ -55,7 +55,7 @@ public MonaiApplicationEntityRepositoryTest(SqliteDatabaseFixture databaseFixtur _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -70,12 +70,13 @@ public async Task GivenAMonaiApplicationEntity_WhenAddingToDatabase_ExpectItToBe AllowedSopClasses = new List { "1", "2", "3" }, Workflows = new List { "W1", "W2" }, Grouping = "G", - IgnoredSopClasses = new List { "4", "5" } + IgnoredSopClasses = new List { "4", "5" }, + PlugInAssemblies = new List { "AssemblyA", "AssemblyB", "AssemblyC" }, }; var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(aet).ConfigureAwait(false); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(false); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(aet.AeTitle, actual!.AeTitle); @@ -92,13 +93,13 @@ public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToRet { var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(false); + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(false); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(result); } @@ -107,12 +108,12 @@ public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturn { var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var actual = await store.FindByNameAsync("AET1").ConfigureAwait(false); + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal("AET1", actual!.AeTitle); Assert.Equal("AET1", actual!.Name); - actual = await store.FindByNameAsync("AET6").ConfigureAwait(false); + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(actual); } @@ -121,23 +122,23 @@ public async Task GivenAMonaiApplicationEntity_WhenRemoveIsCalled_ExpectItToDele { var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET5").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); - var actual = await store.RemoveAsync(expected!).ConfigureAwait(false); + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Same(expected, actual); - var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(false); + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(dbResult); } [Fact] - public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + public async Task GivenMonaiApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() { var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(false); - var actual = await store.ToListAsync().ConfigureAwait(false); + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); } @@ -147,15 +148,15 @@ public async Task GivenAMonaiApplicationEntity_WhenUpdatedIsCalled_ExpectItToSav { var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); expected!.AeTitle = "AET100"; - var actual = await store.UpdateAsync(expected).ConfigureAwait(false); + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); - var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(dbResult); Assert.Equal(expected.AeTitle, dbResult!.AeTitle); } diff --git a/src/Database/EntityFramework/Test/PayloadRepositoryTest.cs b/src/Database/EntityFramework/Test/PayloadRepositoryTest.cs new file mode 100755 index 000000000..6fd5013e1 --- /dev/null +++ b/src/Database/EntityFramework/Test/PayloadRepositoryTest.cs @@ -0,0 +1,223 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Monai.Deploy.Messaging.Events; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [Collection("SqliteDatabase")] + public class PayloadRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public PayloadRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAPayload_WhenAddingToDatabase_ExpectItToBeSaved() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = DataService.DIMSE, Destination = "dest", Source = "source" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "source", "dest")); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling1", "called1")); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling2", "called2")); + payload.State = Payload.PayloadState.Move; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(payload.Key, actual!.Key); + Assert.Equal(payload.State, actual!.State); + Assert.Equal(payload.Count, actual!.Count); + Assert.Equal(payload.RetryCount, actual!.RetryCount); + Assert.Equal(payload.CorrelationId, actual!.CorrelationId); + Assert.Equal(payload.WorkflowInstanceId, actual!.WorkflowInstanceId); + Assert.Equal(payload.TaskId, actual!.TaskId); + Assert.Equal(payload.DataTrigger.Source, actual!.DataTrigger.Source); + Assert.Equal(payload.DataTrigger.Destination, actual!.DataTrigger.Destination); + Assert.Equal(payload.DataTrigger.DataService, actual!.DataTrigger.DataService); + Assert.Equal(payload.Timeout, actual!.Timeout); + Assert.Equal(payload.Files, actual!.Files); + Assert.Equal(payload.DataTrigger, actual.Files[0].DataOrigin); + Assert.Collection(payload.DataOrigins, + item => item.Equals(actual.Files[1]), + item => item.Equals(actual.Files[2])); + } + + [Fact] + public async Task GivenAPayload_WhenRemoveIsCalled_ExpectItToDeleted() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = DataService.DIMSE, Destination = "dest", Source = "source" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling", "called")); + payload.State = Payload.PayloadState.Move; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var added = await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var removed = await store.RemoveAsync(added!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(removed, added); + + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenAPayload_WhenUpdateIsCalled_ExpectItToSaved() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = DataService.DIMSE, Destination = "called", Source = "calling" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling", "called")); + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var added = await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + added.State = Payload.PayloadState.Notify; + added.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.ACR, "calling1", "called1")); + var updated = await store.UpdateAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(updated); + + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.PayloadId == payload.PayloadId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(updated.Key, actual!.Key); + Assert.Equal(updated.State, actual!.State); + Assert.Equal(updated.Count, actual!.Count); + Assert.Equal(updated.RetryCount, actual!.RetryCount); + Assert.Equal(updated.CorrelationId, actual!.CorrelationId); + Assert.Equal(payload.WorkflowInstanceId, actual!.WorkflowInstanceId); + Assert.Equal(payload.TaskId, actual!.TaskId); + Assert.Equal(payload.DataTrigger.Source, actual!.DataTrigger.Source); + Assert.Equal(payload.DataTrigger.Destination, actual!.DataTrigger.Destination); + Assert.Equal(payload.DataTrigger.DataService, actual!.DataTrigger.DataService); + Assert.Equal(updated.Timeout, actual!.Timeout); + Assert.Equal(updated.Files, actual!.Files); + + Assert.Equal(payload.DataTrigger, payload.Files[0].DataOrigin); + + Assert.Collection(payload.DataOrigins, + item => item.Equals(payload.Files[1].DataOrigin)); + } + + [Fact] + public async Task GivenPayloadsInDifferentStates_WhenRemovePendingPayloadsAsyncIsCalled_ExpectPendingPayloadsToBeRemoved() + { + var set = _databaseFixture.DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Move }; + var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + _ = await store.AddAsync(payload1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload4).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload5).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.RemovePendingPayloadsAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result); + + var actual = await set.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(3, actual.Count); + + foreach (var payload in actual) + { + Assert.NotEqual(Payload.PayloadState.Created, payload.State); + } + } + + [Fact] + public async Task GivenPayloadsInDifferentStates_WhenGetPayloadsInStateAsyncIsCalled_ExpectMatchingPayloadsToBeReturned() + { + var set = _databaseFixture.DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + await _databaseFixture.DatabaseContext.SaveChangesAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Move }; + var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + _ = await store.AddAsync(payload1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload4).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload5).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Move).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Single(result); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Created).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result.Count); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result.Count); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify, Payload.PayloadState.Created).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(4, result.Count); + } + } +} diff --git a/src/Database/EntityFramework/Test/SourceApplicationEntityRepositoryTest.cs b/src/Database/EntityFramework/Test/SourceApplicationEntityRepositoryTest.cs old mode 100644 new mode 100755 index 8df19376f..afce0b168 --- a/src/Database/EntityFramework/Test/SourceApplicationEntityRepositoryTest.cs +++ b/src/Database/EntityFramework/Test/SourceApplicationEntityRepositoryTest.cs @@ -32,7 +32,7 @@ public class SourceApplicationEntityRepositoryTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock _serviceScope; private readonly IServiceProvider _serviceProvider; @@ -44,7 +44,7 @@ public SourceApplicationEntityRepositoryTest(SqliteDatabaseFixture databaseFixtu _serviceScopeFactory = new Mock(); _logger = new Mock>(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _serviceScope = new Mock(); var services = new ServiceCollection(); @@ -55,7 +55,7 @@ public SourceApplicationEntityRepositoryTest(SqliteDatabaseFixture databaseFixtu _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -70,8 +70,8 @@ public async Task GivenASourceApplicationEntity_WhenAddingToDatabase_ExpectItToB }; var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(aet).ConfigureAwait(false); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(false); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(aet.AeTitle, actual!.AeTitle); @@ -84,13 +84,13 @@ public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToRet { var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(false); + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(false); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(false); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(result); } @@ -99,27 +99,42 @@ public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturn { var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var actual = await store.FindByNameAsync("AET1").ConfigureAwait(false); + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal("AET1", actual!.AeTitle); Assert.Equal("AET1", actual!.Name); - actual = await store.FindByNameAsync("AET6").ConfigureAwait(false); + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(actual); } + [Fact] + public async Task GivenAAETitleName_WhenFindByAETAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByAETAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual.FirstOrDefault()!.AeTitle); + Assert.Equal("AET1", actual.FirstOrDefault()!.Name); + + actual = await store.FindByAETAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Empty(actual); + } + [Fact] public async Task GivenASourceApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() { var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET5").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); - var actual = await store.RemoveAsync(expected!).ConfigureAwait(false); + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Same(expected, actual); - var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(false); + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(dbResult); } @@ -128,8 +143,8 @@ public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsC { var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(false); - var actual = await store.ToListAsync().ConfigureAwait(false); + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); } @@ -139,15 +154,15 @@ public async Task GivenASourceApplicationEntity_WhenUpdatedIsCalled_ExpectItToSa { var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); - var expected = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(expected); expected!.AeTitle = "AET100"; - var actual = await store.UpdateAsync(expected).ConfigureAwait(false); + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(expected, actual); - var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(false); + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(dbResult); Assert.Equal(expected.AeTitle, dbResult!.AeTitle); } diff --git a/src/Database/EntityFramework/Test/SqliteDatabaseFixture.cs b/src/Database/EntityFramework/Test/SqliteDatabaseFixture.cs old mode 100644 new mode 100755 index aa52fd850..0205695b7 --- a/src/Database/EntityFramework/Test/SqliteDatabaseFixture.cs +++ b/src/Database/EntityFramework/Test/SqliteDatabaseFixture.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ using Microsoft.EntityFrameworkCore; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test { @@ -59,6 +60,25 @@ public void InitDatabaseWithDestinationApplicationEntities() DatabaseContext.SaveChanges(); } + public void InitDatabaseWithHL7DestinationEntities() + { + var aet1 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET1" }; + var aet2 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET2" }; + var aet3 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET3" }; + var aet4 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET4" }; + var aet5 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET5" }; + + var set = DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + set.Add(aet1); + set.Add(aet2); + set.Add(aet3); + set.Add(aet4); + set.Add(aet5); + + DatabaseContext.SaveChanges(); + } + public void InitDatabaseWithMonaiApplicationEntities() { var aet1 = new MonaiApplicationEntity { AeTitle = "AET1", Name = "AET1" }; @@ -78,6 +98,25 @@ public void InitDatabaseWithMonaiApplicationEntities() DatabaseContext.SaveChanges(); } + public void InitDatabaseWithVirtualApplicationEntities() + { + var aet1 = new VirtualApplicationEntity { VirtualAeTitle = "AET1", Name = "AET1" }; + var aet2 = new VirtualApplicationEntity { VirtualAeTitle = "AET2", Name = "AET2" }; + var aet3 = new VirtualApplicationEntity { VirtualAeTitle = "AET3", Name = "AET3" }; + var aet4 = new VirtualApplicationEntity { VirtualAeTitle = "AET4", Name = "AET4" }; + var aet5 = new VirtualApplicationEntity { VirtualAeTitle = "AET5", Name = "AET5" }; + + var set = DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + set.Add(aet1); + set.Add(aet2); + set.Add(aet3); + set.Add(aet4); + set.Add(aet5); + + DatabaseContext.SaveChanges(); + } + public void InitDatabaseWithSourceApplicationEntities() { var aet1 = new SourceApplicationEntity { AeTitle = "AET1", Name = "AET1", HostIp = "1.2.3.4" }; @@ -97,6 +136,36 @@ public void InitDatabaseWithSourceApplicationEntities() DatabaseContext.SaveChanges(); } + internal void InitDatabaseWithDicomAssociationInfoEntries() + { + var da1 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da2 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da3 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da4 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da5 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + + var set = DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + set.Add(da1); + set.Add(da2); + set.Add(da3); + set.Add(da4); + set.Add(da5); + + DatabaseContext.SaveChanges(); + } + internal void InitDatabaseWithExternalAppDetailsEntries() + { + var ea1 = new ExternalAppDetails { StudyInstanceUid = "1", PatientId = "11", PatientIdOutBound = "1" }; + var ea2 = new ExternalAppDetails { StudyInstanceUid = "2", PatientId = "22", PatientIdOutBound = "2" }; + var set = DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + set.Add(ea1); + set.Add(ea2); + + DatabaseContext.SaveChanges(); + } + public void Clear() where T : class { var set = DatabaseContext.Set(); diff --git a/src/Database/EntityFramework/Test/StorageMetadataWrapperRepositoryTest.cs b/src/Database/EntityFramework/Test/StorageMetadataWrapperRepositoryTest.cs old mode 100644 new mode 100755 index 61241f224..405c4d7b6 --- a/src/Database/EntityFramework/Test/StorageMetadataWrapperRepositoryTest.cs +++ b/src/Database/EntityFramework/Test/StorageMetadataWrapperRepositoryTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,10 @@ using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; -using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Monai.Deploy.Messaging.Events; using Moq; namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test @@ -35,7 +36,7 @@ public class StorageMetadataWrapperRepositoryTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock _serviceScope; private readonly IServiceProvider _serviceProvider; @@ -46,7 +47,7 @@ public StorageMetadataWrapperRepositoryTest(SqliteDatabaseFixture databaseFixtur _serviceScopeFactory = new Mock(); _logger = new Mock>(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _serviceScope = new Mock(); var services = new ServiceCollection(); @@ -57,7 +58,7 @@ public StorageMetadataWrapperRepositoryTest(SqliteDatabaseFixture databaseFixtur _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -76,8 +77,8 @@ public async Task GivenADicomStorageMetadataObject_WhenAddingToDatabase_ExpectIt var metadata = CreateMetadataObject(); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(metadata).ConfigureAwait(false); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity.Equals(metadata.Id)).ConfigureAwait(false); + await store.AddAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity.Equals(metadata.Id)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(metadata.CorrelationId, actual!.CorrelationId); @@ -94,8 +95,8 @@ public async Task GivenANonExistedDicomStorageMetadataObject_WhenSavedToDatabase var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddOrUpdateAsync(metadata1).ConfigureAwait(false); - await Assert.ThrowsAsync(async () => await store.UpdateAsync(metadata2).ConfigureAwait(false)).ConfigureAwait(false); + await store.AddOrUpdateAsync(metadata1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await Assert.ThrowsAsync(async () => await store.UpdateAsync(metadata2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); } [Fact] @@ -104,13 +105,13 @@ public async Task GivenAnExistingDicomStorageMetadataObject_WhenUpdated_ExpectIt var metadata = CreateMetadataObject(); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(metadata).ConfigureAwait(false); + await store.AddAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); metadata.SetWorkflows("A", "B", "C"); metadata.File.SetUploaded("bucket"); - await store.AddOrUpdateAsync(metadata).ConfigureAwait(false); + await store.AddOrUpdateAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity.Equals(metadata.Id)).ConfigureAwait(false); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity.Equals(metadata.Id)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(actual); Assert.Equal(metadata.CorrelationId, actual!.CorrelationId); @@ -137,22 +138,26 @@ public async Task GivenACorrelationId_WhenGetFileStorageMetdadataIsCalled_Expect correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - FhirStorageFormat.Json), + FhirStorageFormat.Json, + DataService.FHIR, + "origin"), new FhirFileStorageMetadata( Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - FhirStorageFormat.Json), + FhirStorageFormat.Json, + DataService.FHIR, + "origin"), }; var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); foreach (var item in list) { - await store.AddOrUpdateAsync(item).ConfigureAwait(false); + await store.AddOrUpdateAsync(item).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); } - var results = await store.GetFileStorageMetdadataAsync(correlationId.ToString()).ConfigureAwait(false); + var results = await store.GetFileStorageMetdadataAsync(correlationId.ToString()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(3, results.Count); @@ -163,7 +168,7 @@ public async Task GivenACorrelationId_WhenGetFileStorageMetdadataIsCalled_Expect } [Fact] - public async Task GivenACorrelationIdAndAnIdentity_WhenGetFileStorageMetdadataIsCalled_ExpectMatchingFileStorageMetadataToBeReturned() + public async Task GivenACorrelationIdAndAnIdentity_WhenGetFileStorageMetadadataIsCalled_ExpectMatchingFileStorageMetadataToBeReturned() { var correlationId = Guid.NewGuid().ToString(); var identifier = Guid.NewGuid().ToString(); @@ -172,12 +177,15 @@ public async Task GivenACorrelationIdAndAnIdentity_WhenGetFileStorageMetdadataIs identifier, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DIMSE, + "calling", + "called"); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddOrUpdateAsync(expected).ConfigureAwait(false); + await store.AddOrUpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var match = await store.GetFileStorageMetdadataAsync(correlationId, identifier).ConfigureAwait(false); + var match = await store.GetFileStorageMetdadataAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(match); Assert.Equal(expected.Id, match!.Id); @@ -194,14 +202,17 @@ public async Task GivenACorrelationIdAndAnIdentity_WhenDeleteAsyncIsCalled_Expec identifier, Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DIMSE, + "calling", + "called"); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(expected).ConfigureAwait(false); - var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(false); + await store.AddAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(result); - var stored = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity == identifier).ConfigureAwait(false); + var stored = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity == identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Null(stored); } @@ -213,12 +224,12 @@ public async Task GivenACorrelationIdAndAnIdentity_WhenDeleteAsyncIsCalledWithou var pending = CreateMetadataObject(); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(pending).ConfigureAwait(false); + await store.AddAsync(pending).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(false); + var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.False(result); - var stored = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity == pending.Id).ConfigureAwait(false); + var stored = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Identity == pending.Id).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(stored); } @@ -233,12 +244,12 @@ public async Task GivenStorageMetadataObjects_WhenDeletingPendingUploadsObject_E uploaded.JsonFile.SetUploaded("bucket"); var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); - await store.AddAsync(pending).ConfigureAwait(false); - await store.AddAsync(uploaded).ConfigureAwait(false); + await store.AddAsync(pending).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(uploaded).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - await store.DeletePendingUploadsAsync().ConfigureAwait(false); + await store.DeletePendingUploadsAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); - var result = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(false); + var result = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Single(result); Assert.Equal(uploaded.Id, result[0].Identity); } @@ -250,6 +261,9 @@ public async Task GivenStorageMetadataObjects_WhenDeletingPendingUploadsObject_E Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "callingAET", + "calledAET"); } } diff --git a/src/Database/EntityFramework/Test/VirtualApplicationEntityRepositoryTest.cs b/src/Database/EntityFramework/Test/VirtualApplicationEntityRepositoryTest.cs new file mode 100755 index 000000000..7e2f5f9c7 --- /dev/null +++ b/src/Database/EntityFramework/Test/VirtualApplicationEntityRepositoryTest.cs @@ -0,0 +1,171 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [Collection("SqliteDatabase")] + public class VirtualApplicationEntityRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public VirtualApplicationEntityRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithVirtualApplicationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new VirtualApplicationEntity + { + VirtualAeTitle = "AET", + Name = "AET", + Workflows = new List { "W1", "W2" }, + PlugInAssemblies = new List { "AssemblyA", "AssemblyB", "AssemblyC" }, + }; + + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name.Equals(aet.Name)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.VirtualAeTitle, actual!.VirtualAeTitle); + Assert.Equal(aet.Name, actual!.Name); + Assert.Equal(aet.Workflows, actual!.Workflows); + Assert.Equal(aet.PlugInAssemblies, actual!.PlugInAssemblies); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var result = await store.ContainsAsync(p => p.VirtualAeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.VirtualAeTitle.Equals("AET1", StringComparison.Ordinal)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.VirtualAeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByAeTitleAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByAeTitleAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.VirtualAeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByAeTitleAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByAeTitleAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Name == "AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenVirtualApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await _databaseFixture.DatabaseContext.Set().ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Equal(expected, actual); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByAeTitleAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.VirtualAeTitle = "AET100"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByAeTitleAsync("AET100").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.VirtualAeTitle, dbResult!.VirtualAeTitle); + } + } +} diff --git a/src/Database/EntityFramework/Test/packages.lock.json b/src/Database/EntityFramework/Test/packages.lock.json index 4f9e1e560..caed38f23 100644 --- a/src/Database/EntityFramework/Test/packages.lock.json +++ b/src/Database/EntityFramework/Test/packages.lock.json @@ -1,107 +1,116 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.EntityFrameworkCore.InMemory": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "2h5Pyy5e5EJhvMVl1UGPTLst1Q/+8rwEIvjsFwQDrsOmbsgzlbkvCJM2K89wvjA3UKAn5nTyRxCzKu9MMaJYkg==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "q0/yGN42dy/ph9tV1ecuvrzpfkGuGdjyfdWNSXylQ0CDUidBCG6o+srOyOPg32gbjV0nLRv9YFkrS4xprXwAew==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11" + "Microsoft.EntityFrameworkCore": "8.0.14" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -109,535 +118,434 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, - "NETStandard.Library": { + "Mono.TextTemplating": { "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "NuGet.Frameworks": { + "NLog": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "Polly": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "Polly.Core": "8.5.2" } }, - "runtime.native.System.Security.Cryptography.Apple": { + "Polly.Core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", - "dependencies": { - "SQLitePCLRaw.core": "2.0.6" - } - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "System.Runtime": "4.3.0" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -645,32 +553,10 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { + "System.CodeDom": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -680,387 +566,84 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" } }, - "System.Diagnostics.EventLog": { + "System.Composition.AttributedModel": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" - }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Diagnostics.Tracing": { + "System.Composition.Convention": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.AttributedModel": "6.0.0" } }, - "System.Globalization": { + "System.Composition.Hosting": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.Runtime": "6.0.0" } }, - "System.Globalization.Calendars": { + "System.Composition.Runtime": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" }, - "System.Globalization.Extensions": { + "System.Composition.TypedParts": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, - "System.IO": { + "System.Diagnostics.EventLog": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Linq": { + "System.IO.Pipelines": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "System.Collections.Immutable": "6.0.0" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -1068,350 +651,37 @@ "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" - }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Xml.ReaderWriter": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Xml.XDocument": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, "xunit.abstractions": { @@ -1421,95 +691,86 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } } } diff --git a/src/Database/EntityFramework/packages.lock.json b/src/Database/EntityFramework/packages.lock.json index ffc351e68..7dafede81 100644 --- a/src/Database/EntityFramework/packages.lock.json +++ b/src/Database/EntityFramework/packages.lock.json @@ -1,124 +1,135 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Microsoft.EntityFrameworkCore": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "2H9vcmv9PIboVMisL0izD+xaUaWeZ/jqqRJeaD8MKIDaKwZsdHE790Sa4fAHjZjzVgoraELRI7CscjPMfLE2jA==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", "dependencies": { - "Humanizer.Core": "2.8.26", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, - "Microsoft.Extensions.Configuration": { + "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Configuration.FileExtensions": { + "Microsoft.Extensions.Configuration.Json": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, - "Microsoft.Extensions.Configuration.Json": { + "Polly": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "requested": "[8.5.2, )", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" + "Polly.Core": "8.5.2" } }, - "Ardalis.GuardClauses": { - "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "Polly": { + "type": "Direct", + "requested": "[8.5.2, )", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "JetBrains.Annotations": "2021.3.0" + "Polly.Core": "8.5.2" } }, + "Ardalis.GuardClauses": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "Humanizer.Core": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2.8.26", - "contentHash": "OiKusGL20vby4uDEswj2IgkdchC1yQ6rwbIkZDVBPIR6al2b7n3pC91elBul9q33KaBgRKhbZH3+2Ur4fnWx2A==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, - "JetBrains.Annotations": { + "Humanizer.Core": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -126,270 +137,358 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" - }, - "Microsoft.Extensions.Options": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.NETCore.Platforms": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Toolkit.HighPerformance": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Polly": { + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -397,6 +496,11 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" + }, "System.Collections.Immutable": { "type": "Transitive", "resolved": "6.0.0", @@ -405,28 +509,80 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { + "System.Composition": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition.Convention": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", + "dependencies": { + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -435,77 +591,70 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } } } diff --git a/src/Database/Monai.Deploy.InformaticsGateway.Database.csproj b/src/Database/Monai.Deploy.InformaticsGateway.Database.csproj old mode 100644 new mode 100755 index d4fd80ccc..6b3344283 --- a/src/Database/Monai.Deploy.InformaticsGateway.Database.csproj +++ b/src/Database/Monai.Deploy.InformaticsGateway.Database.csproj @@ -1,4 +1,4 @@ - - - Monai.Deploy.InformaticsGateway.Database - net6.0 + net8.0 Apache-2.0 ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + enable + false - @@ -31,43 +30,36 @@ - - - - - - - - All - - - - - - + - + + + + + + + \ No newline at end of file diff --git a/src/Database/MongoDB/Configurations/DestinationApplicationEntityConfiguration.cs b/src/Database/MongoDB/Configurations/DestinationApplicationEntityConfiguration.cs old mode 100644 new mode 100755 index 25ceefb39..9f1c36e21 --- a/src/Database/MongoDB/Configurations/DestinationApplicationEntityConfiguration.cs +++ b/src/Database/MongoDB/Configurations/DestinationApplicationEntityConfiguration.cs @@ -15,7 +15,7 @@ * limitations under the License. */ -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using MongoDB.Bson.Serialization; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations diff --git a/src/Database/MongoDB/Configurations/DicomAssociationInfoConfiguration.cs b/src/Database/MongoDB/Configurations/DicomAssociationInfoConfiguration.cs new file mode 100755 index 000000000..4b6b10034 --- /dev/null +++ b/src/Database/MongoDB/Configurations/DicomAssociationInfoConfiguration.cs @@ -0,0 +1,34 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; +using MongoDB.Bson.Serialization; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations +{ + internal static class DicomAssociationInfoConfiguration + { + public static void Configure() + { + BsonClassMap.RegisterClassMap(j => + { + j.AutoMap(); + j.SetIgnoreExtraElements(true); + }); + } + } +} diff --git a/src/Database/MongoDB/Configurations/ExternalAppDetailsConfiguration.cs b/src/Database/MongoDB/Configurations/ExternalAppDetailsConfiguration.cs new file mode 100755 index 000000000..7a6fea639 --- /dev/null +++ b/src/Database/MongoDB/Configurations/ExternalAppDetailsConfiguration.cs @@ -0,0 +1,33 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; +using MongoDB.Bson.Serialization; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations +{ + internal static class ExternalAppDetailsConfiguration + { + public static void Configure() + { + BsonClassMap.RegisterClassMap(j => + { + j.AutoMap(); + j.SetIgnoreExtraElements(true); + }); + } + } +} diff --git a/src/Database/MongoDB/Configurations/HL7DestinationEntityConfiguration.cs b/src/Database/MongoDB/Configurations/HL7DestinationEntityConfiguration.cs new file mode 100644 index 000000000..a91466b05 --- /dev/null +++ b/src/Database/MongoDB/Configurations/HL7DestinationEntityConfiguration.cs @@ -0,0 +1,34 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api.Models; +using MongoDB.Bson.Serialization; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations +{ + internal static class HL7DestinationEntityConfiguration + { + public static void Configure() + { + BsonClassMap.RegisterClassMap(j => + { + j.AutoMap(); + j.SetIgnoreExtraElements(true); + }); + } + } +} diff --git a/src/Database/MongoDB/Configurations/InferenceRequestConfiguration.cs b/src/Database/MongoDB/Configurations/InferenceRequestConfiguration.cs index caa7721f5..9683ce426 100644 --- a/src/Database/MongoDB/Configurations/InferenceRequestConfiguration.cs +++ b/src/Database/MongoDB/Configurations/InferenceRequestConfiguration.cs @@ -16,10 +16,7 @@ */ using Monai.Deploy.InformaticsGateway.Api.Rest; -using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Serializers; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations { @@ -30,9 +27,8 @@ public static void Configure() BsonClassMap.RegisterClassMap(j => { j.AutoMap(); - j.MapIdMember(c => c.InferenceRequestId) - .SetIdGenerator(GuidGenerator.Instance) - .SetSerializer(new GuidSerializer(BsonType.String)); + j.SetIdMember(j.GetMemberMap(c => c.InferenceRequestId)); + j.MapIdMember(c => c.InferenceRequestId); j.SetIgnoreExtraElements(true); j.UnmapProperty(p => p.Application); diff --git a/src/Database/MongoDB/Configurations/MonaiApplicationEntityConfiguration.cs b/src/Database/MongoDB/Configurations/MonaiApplicationEntityConfiguration.cs old mode 100644 new mode 100755 index 4a8e2a65d..3f1eaca0e --- a/src/Database/MongoDB/Configurations/MonaiApplicationEntityConfiguration.cs +++ b/src/Database/MongoDB/Configurations/MonaiApplicationEntityConfiguration.cs @@ -15,11 +15,8 @@ * limitations under the License. */ -using Monai.Deploy.InformaticsGateway.Api; -using MongoDB.Bson; +using Monai.Deploy.InformaticsGateway.Api.Models; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Serializers; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations { diff --git a/src/Database/MongoDB/Configurations/MongoDBEntityBaseConfiguration.cs b/src/Database/MongoDB/Configurations/MongoDBEntityBaseConfiguration.cs old mode 100644 new mode 100755 index 3713e7430..9abd8b535 --- a/src/Database/MongoDB/Configurations/MongoDBEntityBaseConfiguration.cs +++ b/src/Database/MongoDB/Configurations/MongoDBEntityBaseConfiguration.cs @@ -15,11 +15,8 @@ * limitations under the License. */ -using Monai.Deploy.InformaticsGateway.Api; -using MongoDB.Bson; +using Monai.Deploy.InformaticsGateway.Api.Storage; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Serializers; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations { @@ -30,9 +27,9 @@ public static void Configure() BsonClassMap.RegisterClassMap(j => { j.SetIsRootClass(true); - j.MapIdMember(c => c.Id) - .SetIdGenerator(GuidGenerator.Instance) - .SetSerializer(new GuidSerializer(BsonType.String)); + j.SetIdMember(j.GetMemberMap(c => c.Id)); + j.MapIdMember(c => c.Id); + j.MapMember(c => c.DateTimeCreated); }); } } diff --git a/src/Database/MongoDB/Configurations/PayloadConfiguration.cs b/src/Database/MongoDB/Configurations/PayloadConfiguration.cs index 447c9fec5..6fccc942c 100644 --- a/src/Database/MongoDB/Configurations/PayloadConfiguration.cs +++ b/src/Database/MongoDB/Configurations/PayloadConfiguration.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,7 @@ */ using Monai.Deploy.InformaticsGateway.Api.Storage; -using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.IdGenerators; -using MongoDB.Bson.Serialization.Serializers; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations { @@ -29,13 +26,11 @@ public static void Configure() BsonClassMap.RegisterClassMap(j => { j.AutoMap(); - j.MapIdMember(c => c.PayloadId) - .SetIdGenerator(GuidGenerator.Instance) - .SetSerializer(new GuidSerializer(BsonType.String)); + j.SetIdMember(j.GetMemberMap(c => c.PayloadId)); + j.MapIdProperty(j => j.PayloadId); + j.SetIgnoreExtraElements(true); - j.UnmapProperty(p => p.CalledAeTitle); - j.UnmapProperty(p => p.CallingAeTitle); j.UnmapProperty(p => p.HasTimedOut); j.UnmapProperty(p => p.Elapsed); j.UnmapProperty(p => p.Count); @@ -44,7 +39,6 @@ public static void Configure() BsonClassMap.RegisterClassMap(); BsonClassMap.RegisterClassMap(); BsonClassMap.RegisterClassMap(); - } } -} +} \ No newline at end of file diff --git a/src/Database/MongoDB/Configurations/VirtualApplicationEntityConfiguration.cs b/src/Database/MongoDB/Configurations/VirtualApplicationEntityConfiguration.cs new file mode 100644 index 000000000..3fc059408 --- /dev/null +++ b/src/Database/MongoDB/Configurations/VirtualApplicationEntityConfiguration.cs @@ -0,0 +1,33 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Api; +using MongoDB.Bson.Serialization; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations +{ + internal static class VirtualApplicationEntityConfiguration + { + public static void Configure() + { + BsonClassMap.RegisterClassMap(j => + { + j.AutoMap(); + j.SetIgnoreExtraElements(true); + }); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/DestinationApplicationEntityRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/DestinationApplicationEntityRepositoryTest.cs new file mode 100755 index 000000000..67ee24c0b --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/DestinationApplicationEntityRepositoryTest.cs @@ -0,0 +1,165 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class DestinationApplicationEntityRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public DestinationApplicationEntityRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithDestinationApplicationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenADestinationApplicationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new DestinationApplicationEntity { AeTitle = "AET", HostIp = "1.2.3.4", Port = 114, Name = "AET" }; + + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(DestinationApplicationEntity)); + var actual = await collection.Find(p => p.Name == aet.Name).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.AeTitle, actual!.AeTitle); + Assert.Equal(aet.HostIp, actual!.HostIp); + Assert.Equal(aet.Port, actual!.Port); + Assert.Equal(aet.Name, actual!.Name); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1")).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.AeTitle != "AET1" && p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 999).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.AeTitle); + Assert.Equal("1.2.3.4", actual!.HostIp); + Assert.Equal(114, actual!.Port); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenADestinationApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(DestinationApplicationEntity)); + var dbResult = await collection.Find(p => p.Name == "AET5").FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(DestinationApplicationEntity)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenADestinationApplicationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new DestinationApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.AeTitle = "AET100"; + expected!.Port = 1000; + expected!.HostIp = "loalhost"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.AeTitle, dbResult!.AeTitle); + Assert.Equal(expected.HostIp, dbResult!.HostIp); + Assert.Equal(expected.Port, dbResult!.Port); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/DicomAssociationInfoRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/DicomAssociationInfoRepositoryTest.cs new file mode 100755 index 000000000..f57d27728 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/DicomAssociationInfoRepositoryTest.cs @@ -0,0 +1,123 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class DicomAssociationInfoRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public DicomAssociationInfoRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithDicomAssociationInfoEntries(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenADicomAssociationInfo_WhenAddingToDatabase_ExpectItToBeSaved() + { + var association = new DicomAssociationInfo { CalledAeTitle = "called", CallingAeTitle = "calling", CorrelationId = Guid.NewGuid().ToString(), DateTimeCreated = DateTime.UtcNow, RemoteHost = "host", RemotePort = 100 }; + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(Guid.NewGuid().ToString()); + association.FileReceived(null); + association.FileReceived(string.Empty); + association.Disconnect(); + + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(association).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(DicomAssociationInfo)); + var actual = await collection.Find(p => p.Id == association.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(association.FileCount, actual!.FileCount); + Assert.Equal(association.Duration, actual!.Duration); + Assert.Equal(association.CalledAeTitle, actual!.CalledAeTitle); + Assert.Equal(association.CallingAeTitle, actual!.CallingAeTitle); + Assert.Equal(association.CorrelationId, actual!.CorrelationId); + + actual!.DateTimeCreated.Should().BeCloseTo(association.DateTimeCreated, TimeSpan.FromMilliseconds(500)); + actual!.DateTimeDisconnected.Should().BeCloseTo(association.DateTimeDisconnected, TimeSpan.FromMilliseconds(500)); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenGetAllAsyncCalled_ExpectLimitedEntitiesToBeReturned() + { + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(DicomAssociationInfo)); + var startTime = DateTime.Now; + var endTime = DateTime.MinValue; + var builder = Builders.Filter; + var filter = builder.Empty; + filter &= builder.Where(t => t.DateTimeDisconnected >= startTime.ToUniversalTime()); + filter &= builder.Where(t => t.DateTimeDisconnected <= endTime.ToUniversalTime()); + var expected = await collection.Find(filter).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.GetAllAsync(0, 1, startTime, endTime, default).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().NotBeNull(); + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new DicomAssociationInfoRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var collection = _databaseFixture.Database.GetCollection(nameof(DicomAssociationInfo)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/ExternalAppDetailsRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/ExternalAppDetailsRepositoryTest.cs new file mode 100755 index 000000000..5cfb34995 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/ExternalAppDetailsRepositoryTest.cs @@ -0,0 +1,136 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class ExternalAppDetailsRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public ExternalAppDetailsRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithExternalAppEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenExternalAppDetailsEntitiesInTheDatabase_WhenGetAsyncCalled_ExpectEntitieToBeReturned() + { + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(ExternalAppDetails)); + + var expected = (await collection.FindAsync(e => e.StudyInstanceUid == "2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).First(); + var actual = (await store.GetAsync("2", new CancellationToken()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).FirstOrDefault(); + + actual.Should().NotBeNull(); + Assert.Equal(expected.StudyInstanceUid, actual!.StudyInstanceUid); + Assert.Equal(expected.ExportTaskID, actual!.ExportTaskID); + Assert.Equal(expected.CorrelationId, actual!.CorrelationId); + Assert.Equal(expected.WorkflowInstanceId, actual!.WorkflowInstanceId); + } + + [Fact] + public async Task GivenAExternalAppDetails_WhenAddingToDatabase_ExpectItToBeSaved() + { + var app = new ExternalAppDetails { StudyInstanceUid = "3", ExportTaskID = "ExportTaskID3", CorrelationId = "CorrelationId3", WorkflowInstanceId = "WorkflowInstanceId3" }; + + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(app, new CancellationToken()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(ExternalAppDetails)); + var actual = await collection.Find(p => p.Id == app.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(app.StudyInstanceUid, actual!.StudyInstanceUid); + Assert.Equal(app.ExportTaskID, actual!.ExportTaskID); + Assert.Equal(app.CorrelationId, actual!.CorrelationId); + Assert.Equal(app.WorkflowInstanceId, actual!.WorkflowInstanceId); + + actual!.DateTimeCreated.Should().BeCloseTo(app.DateTimeCreated, TimeSpan.FromMilliseconds(500)); + } + + [Fact] + public async Task GivenExternalAppDetailsEntitiesInTheDatabase_WhenGetPatientOutboundAsyncCalled_ExpectEntitieToBeReturned() + { + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(ExternalAppDetails)); + + var expected = (await collection.FindAsync(e => e.PatientIdOutBound == "pat1out1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).First(); + var actual = (await store.GetByPatientIdOutboundAsync("pat1out1", new CancellationToken()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + actual.Should().NotBeNull(); + Assert.Equal(expected.StudyInstanceUid, actual!.StudyInstanceUid); + Assert.Equal(expected.ExportTaskID, actual!.ExportTaskID); + Assert.Equal(expected.CorrelationId, actual!.CorrelationId); + Assert.Equal(expected.WorkflowInstanceId, actual!.WorkflowInstanceId); + } + + [Fact] + public async Task GivenExternalAppDetailsEntitiesInTheDatabase_WhenGetStudyIdOutboundAsyncCalled_ExpectEntitieToBeReturned() + { + var store = new ExternalAppDetailsRepository(_serviceScopeFactory.Object, _logger.Object, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(ExternalAppDetails)); + + var expected = (await collection.FindAsync(e => e.StudyInstanceUidOutBound == "sudIdOut2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).First(); + var actual = (await store.GetByStudyIdOutboundAsync("sudIdOut2", new CancellationToken()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + actual.Should().NotBeNull(); + Assert.Equal(expected.StudyInstanceUid, actual!.StudyInstanceUid); + Assert.Equal(expected.ExportTaskID, actual!.ExportTaskID); + Assert.Equal(expected.CorrelationId, actual!.CorrelationId); + Assert.Equal(expected.WorkflowInstanceId, actual!.WorkflowInstanceId); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/HL7DestinationEntityRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/HL7DestinationEntityRepositoryTest.cs new file mode 100755 index 000000000..a2a67f981 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/HL7DestinationEntityRepositoryTest.cs @@ -0,0 +1,157 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class HL7DestinationEntityRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public HL7DestinationEntityRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithHL7DestinationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = [1, 1, 1]; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET" }; + + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(HL7DestinationEntity)); + var actual = await collection.Find(p => p.Name == aet.Name).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.HostIp, actual!.HostIp); + Assert.Equal(aet.Port, actual!.Port); + Assert.Equal(aet.Name, actual!.Name); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + var result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 114 && p.HostIp == "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Port == 999).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("1.2.3.4", actual!.HostIp); + Assert.Equal(114, actual!.Port); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(HL7DestinationEntity)); + var dbResult = await collection.Find(p => p.Name == "AET5").FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenHL7DestinationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var collection = _databaseFixture.Database.GetCollection(nameof(HL7DestinationEntity)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenAHL7DestinationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new HL7DestinationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options, _databaseFixture.Options); + + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.Port = 1000; + expected!.HostIp = "loalhost"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.HostIp, dbResult!.HostIp); + Assert.Equal(expected.Port, dbResult!.Port); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/InferenceRequestRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/InferenceRequestRepositoryTest.cs new file mode 100755 index 000000000..7c982eef8 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/InferenceRequestRepositoryTest.cs @@ -0,0 +1,271 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class InferenceRequestRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public InferenceRequestRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture; + _databaseFixture.InitDatabaseWithInferenceRequests(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAnInferenceRequest_WhenAddingToDatabase_ExpectItToBeSaved() + { + var inferenceRequest = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + var actual = await collection.Find(p => p.InferenceRequestId.Equals(inferenceRequest.InferenceRequestId)).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(inferenceRequest.InferenceRequestId, actual!.InferenceRequestId); + Assert.Equal(inferenceRequest.State, actual!.State); + Assert.Equal(inferenceRequest.Status, actual!.Status); + Assert.Equal(inferenceRequest.TransactionId, actual!.TransactionId); + Assert.Equal(inferenceRequest.TryCount, actual!.TryCount); + } + + [Fact] + public async Task GivenAFailedInferenceRequstThatExceededRetries_WhenUpdateIsCalled_ShallMarkAsFailed() + { + var inferenceRequest = new InferenceRequest + { + TransactionId = Guid.NewGuid().ToString(), + TryCount = 3 + }; + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + var result = await collection.Find(p => p.TransactionId == inferenceRequest.TransactionId).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(result); + Assert.Equal(InferenceRequestState.Completed, result!.State); + Assert.Equal(InferenceRequestStatus.Fail, result!.Status); + } + + [Fact] + public async Task GivenAFailedInferenceRequst_WhenUpdateIsCalled_ShallRetryLater() + { + var inferenceRequest = new InferenceRequest + { + TransactionId = Guid.NewGuid().ToString(), + TryCount = 1 + }; + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Fail).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + var result = await collection.Find(Builders.Filter.Where(p => p.TransactionId == inferenceRequest.TransactionId)).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(InferenceRequestState.Queued, result!.State); + Assert.Equal(InferenceRequestStatus.Unknown, result!.Status); + Assert.Equal(2, result!.TryCount); + } + + [Fact] + public async Task GivenASuccessfulInferenceRequest_WhenUpdateIsCalled_ShallMarkAsCompleted() + { + var inferenceRequest = new InferenceRequest + { + TransactionId = Guid.NewGuid().ToString() + }; + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.UpdateAsync(inferenceRequest, InferenceRequestStatus.Success).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + var result = await collection.Find(Builders.Filter.Where(p => p.TransactionId == inferenceRequest.TransactionId)).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(result); + Assert.Equal(InferenceRequestState.Completed, result!.State); + Assert.Equal(InferenceRequestStatus.Success, result!.Status); + } + + [Fact] + public async Task GivenAQueuedInferenceRequests_WhenTakeIsCalled_ShallReturnFirstQueued() + { + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + MongoDatabaseFixture.Clear(collection); + + var inferenceRequestInProcess = CreateInferenceRequest(InferenceRequestState.InProcess); + var inferenceRequestCompleted = CreateInferenceRequest(InferenceRequestState.Completed); + var inferenceRequestQueued = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestQueued).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var actual = await store.TakeAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(inferenceRequestQueued.InferenceRequestId, actual!.InferenceRequestId); + Assert.Equal(InferenceRequestState.InProcess, actual!.State); + Assert.Equal(inferenceRequestQueued.Status, actual!.Status); + Assert.Equal(inferenceRequestQueued.TransactionId, actual!.TransactionId); + Assert.Equal(inferenceRequestQueued.TryCount, actual!.TryCount); + } + + [Fact] + public async Task GivenNoQueuedInferenceRequests_WhenTakeIsCalled_ShallReturnNotReturnAnything() + { + var collection = _databaseFixture.Database.GetCollection(nameof(InferenceRequest)); + MongoDatabaseFixture.Clear(collection); + + var cancellationTokenSource = new CancellationTokenSource(); + var inferenceRequestInProcess = CreateInferenceRequest(InferenceRequestState.InProcess); + var inferenceRequestCompleted = CreateInferenceRequest(InferenceRequestState.Completed); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequestInProcess).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequestCompleted).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + cancellationTokenSource.CancelAfter(500); + await Assert.ThrowsAsync(async () => await store.TakeAsync(cancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + } + + [Fact] + public async Task GivenInferenceRequests_WhenGetInferenceRequestIsCalled_ShallReturnMatchingObject() + { + var inferenceRequest1 = CreateInferenceRequest(); + var inferenceRequest2 = CreateInferenceRequest(); + var inferenceRequest3 = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequest2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(inferenceRequest3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.GetInferenceRequestAsync(inferenceRequest1.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest1.TransactionId, result!.TransactionId); + result = await store.GetInferenceRequestAsync(inferenceRequest2.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest2.TransactionId, result!.TransactionId); + result = await store.GetInferenceRequestAsync(inferenceRequest3.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest3.TransactionId, result!.TransactionId); + + result = await store.GetInferenceRequestAsync(inferenceRequest1.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest1.TransactionId, result!.TransactionId); + result = await store.GetInferenceRequestAsync(inferenceRequest2.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest2.TransactionId, result!.TransactionId); + result = await store.GetInferenceRequestAsync(inferenceRequest3.InferenceRequestId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(result); + Assert.Equal(inferenceRequest3.TransactionId, result!.TransactionId); + } + + [Fact] + public async Task GivenInferenceRequests_WhenExistsCalled_ShallReturnCorrectValue() + { + var inferenceRequest = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.ExistsAsync(inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + + result = await store.ExistsAsync("random").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAMatchingInferenceRequest_WhenGetStatusCalled_ShallReturnStatus() + { + var inferenceRequest = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.GetStatusAsync(inferenceRequest.TransactionId).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(result); + Assert.Equal(inferenceRequest.TransactionId, result!.TransactionId); + } + + [Fact] + public async Task GivenNoMatchingInferenceRequest_WhenGetStatusCalled_ShallReturnStatus() + { + var inferenceRequest = CreateInferenceRequest(); + + var store = new InferenceRequestRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(inferenceRequest).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.GetStatusAsync("bogus").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Null(result); + } + + private static InferenceRequest CreateInferenceRequest(InferenceRequestState state = InferenceRequestState.Queued) => new() + { + InferenceRequestId = Guid.NewGuid(), + State = state, + Status = InferenceRequestStatus.Unknown, + TransactionId = Guid.NewGuid().ToString(), + TryCount = 0, + }; + } +} diff --git a/src/Database/MongoDB/Integration.Test/Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test.csproj b/src/Database/MongoDB/Integration.Test/Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test.csproj new file mode 100644 index 000000000..548ef0b93 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test.csproj @@ -0,0 +1,42 @@ + + + + Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test + net8.0 + enable + enable + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + \ No newline at end of file diff --git a/src/Database/MongoDB/Integration.Test/MonaiApplicationEntityRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/MonaiApplicationEntityRepositoryTest.cs new file mode 100755 index 000000000..880c11461 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/MonaiApplicationEntityRepositoryTest.cs @@ -0,0 +1,169 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class MonaiApplicationEntityRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public MonaiApplicationEntityRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithMonaiApplicationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAMonaiApplicationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new MonaiApplicationEntity + { + AeTitle = "AET", + Name = "AET", + Timeout = 100, + AllowedSopClasses = new List { "1", "2", "3" }, + Workflows = new List { "W1", "W2" }, + Grouping = "G", + IgnoredSopClasses = new List { "4", "5" } + }; + + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(MonaiApplicationEntity)); + var actual = await collection.Find(p => p.Name == aet.Name).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.AeTitle, actual!.AeTitle); + Assert.Equal(aet.Name, actual!.Name); + Assert.Equal(aet.Timeout, actual!.Timeout); + Assert.Equal(aet.AllowedSopClasses, actual!.AllowedSopClasses); + Assert.Equal(aet.Workflows, actual!.Workflows); + Assert.Equal(aet.Grouping, actual!.Grouping); + Assert.Equal(aet.IgnoredSopClasses, actual!.IgnoredSopClasses); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1")).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.AeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAMonaiApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(MonaiApplicationEntity)); + var dbResult = await collection.Find(p => p.Name == "AET5").FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var collection = _databaseFixture.Database.GetCollection(nameof(MonaiApplicationEntity)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenAMonaiApplicationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new MonaiApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.AeTitle = "AET100"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.AeTitle, dbResult!.AeTitle); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/MongoDatabaseFixture.cs b/src/Database/MongoDB/Integration.Test/MongoDatabaseFixture.cs new file mode 100755 index 000000000..140ea64a8 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/MongoDatabaseFixture.cs @@ -0,0 +1,176 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.MongoDB; +using MongoDB.Driver; + +namespace Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test +{ + [CollectionDefinition("MongoDatabase")] + public class MongoDatabaseCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } + + public class MongoDatabaseFixture + { + public IMongoClient Client { get; set; } + public IMongoDatabase Database { get; set; } + public IOptions Options { get; set; } + + public MongoDatabaseFixture() + { + Client = new MongoClient("mongodb://root:rootpassword@localhost:27017"); + Options = Microsoft.Extensions.Options.Options.Create(new DatabaseOptions { DatabaseName = $"IGTest" }); + Database = Client.GetDatabase(Options.Value.DatabaseName); + + var migration = new MongoDatabaseMigrationManager(); + migration.Migrate(null!); + } + + public void InitDatabaseWithDestinationApplicationEntities() + { + var collection = Database.GetCollection(nameof(DestinationApplicationEntity)); + Clear(collection); + var aet1 = new DestinationApplicationEntity { AeTitle = "AET1", HostIp = "1.2.3.4", Port = 114, Name = "AET1", DateTimeCreated = DateTime.UtcNow }; + var aet2 = new DestinationApplicationEntity { AeTitle = "AET2", HostIp = "1.2.3.4", Port = 114, Name = "AET2", DateTimeCreated = DateTime.UtcNow }; + var aet3 = new DestinationApplicationEntity { AeTitle = "AET3", HostIp = "1.2.3.4", Port = 114, Name = "AET3", DateTimeCreated = DateTime.UtcNow }; + var aet4 = new DestinationApplicationEntity { AeTitle = "AET4", HostIp = "1.2.3.4", Port = 114, Name = "AET4", DateTimeCreated = DateTime.UtcNow }; + var aet5 = new DestinationApplicationEntity { AeTitle = "AET5", HostIp = "1.2.3.4", Port = 114, Name = "AET5", DateTimeCreated = DateTime.UtcNow }; + + collection.InsertOne(aet1); + collection.InsertOne(aet2); + collection.InsertOne(aet3); + collection.InsertOne(aet4); + collection.InsertOne(aet5); + } + + public void InitDatabaseWithHL7DestinationEntities() + { + var collection = Database.GetCollection(nameof(HL7DestinationEntity)); + Clear(collection); + var aet1 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET1", DateTimeCreated = DateTime.UtcNow }; + var aet2 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET2", DateTimeCreated = DateTime.UtcNow }; + var aet3 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET3", DateTimeCreated = DateTime.UtcNow }; + var aet4 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET4", DateTimeCreated = DateTime.UtcNow }; + var aet5 = new HL7DestinationEntity { HostIp = "1.2.3.4", Port = 114, Name = "AET5", DateTimeCreated = DateTime.UtcNow }; + + collection.InsertOne(aet1); + collection.InsertOne(aet2); + collection.InsertOne(aet3); + collection.InsertOne(aet4); + collection.InsertOne(aet5); + } + + public void InitDatabaseWithMonaiApplicationEntities() + { + var collection = Database.GetCollection(nameof(MonaiApplicationEntity)); + Clear(collection); + var aet1 = new MonaiApplicationEntity { AeTitle = "AET1", Name = "AET1", DateTimeCreated = DateTime.UtcNow }; + var aet2 = new MonaiApplicationEntity { AeTitle = "AET2", Name = "AET2", DateTimeCreated = DateTime.UtcNow }; + var aet3 = new MonaiApplicationEntity { AeTitle = "AET3", Name = "AET3", DateTimeCreated = DateTime.UtcNow }; + var aet4 = new MonaiApplicationEntity { AeTitle = "AET4", Name = "AET4", DateTimeCreated = DateTime.UtcNow }; + var aet5 = new MonaiApplicationEntity { AeTitle = "AET5", Name = "AET5", DateTimeCreated = DateTime.UtcNow }; + + collection.InsertOne(aet1); + collection.InsertOne(aet2); + collection.InsertOne(aet3); + collection.InsertOne(aet4); + collection.InsertOne(aet5); + } + + public void InitDatabaseWithVirtualApplicationEntities() + { + var collection = Database.GetCollection(nameof(VirtualApplicationEntity)); + Clear(collection); + var aet1 = new VirtualApplicationEntity { VirtualAeTitle = "AET1", Name = "AET1", DateTimeCreated = DateTime.UtcNow }; + var aet2 = new VirtualApplicationEntity { VirtualAeTitle = "AET2", Name = "AET2", DateTimeCreated = DateTime.UtcNow }; + var aet3 = new VirtualApplicationEntity { VirtualAeTitle = "AET3", Name = "AET3", DateTimeCreated = DateTime.UtcNow }; + var aet4 = new VirtualApplicationEntity { VirtualAeTitle = "AET4", Name = "AET4", DateTimeCreated = DateTime.UtcNow }; + var aet5 = new VirtualApplicationEntity { VirtualAeTitle = "AET5", Name = "AET5", DateTimeCreated = DateTime.UtcNow }; + + collection.InsertOne(aet1); + collection.InsertOne(aet2); + collection.InsertOne(aet3); + collection.InsertOne(aet4); + collection.InsertOne(aet5); + } + + public void InitDatabaseWithSourceApplicationEntities() + { + var collection = Database.GetCollection(nameof(SourceApplicationEntity)); + Clear(collection); + var aet1 = new SourceApplicationEntity { AeTitle = "AET1", Name = "AET1", HostIp = "1.2.3.4", DateTimeCreated = DateTime.UtcNow }; + var aet2 = new SourceApplicationEntity { AeTitle = "AET2", Name = "AET2", HostIp = "1.2.3.4", DateTimeCreated = DateTime.UtcNow }; + var aet3 = new SourceApplicationEntity { AeTitle = "AET3", Name = "AET3", HostIp = "1.2.3.4", DateTimeCreated = DateTime.UtcNow }; + var aet4 = new SourceApplicationEntity { AeTitle = "AET4", Name = "AET4", HostIp = "1.2.3.4", DateTimeCreated = DateTime.UtcNow }; + var aet5 = new SourceApplicationEntity { AeTitle = "AET5", Name = "AET5", HostIp = "1.2.3.4", DateTimeCreated = DateTime.UtcNow }; + + collection.InsertOne(aet1); + collection.InsertOne(aet2); + collection.InsertOne(aet3); + collection.InsertOne(aet4); + collection.InsertOne(aet5); + } + public void InitDatabaseWithExternalAppEntities() + { + var collection = Database.GetCollection(nameof(ExternalAppDetails)); + Clear(collection); + + var ea1 = new ExternalAppDetails { StudyInstanceUid = "1", ExportTaskID = "ExportTaskID", CorrelationId = "CorrelationId", WorkflowInstanceId = "WorkflowInstanceId", PatientIdOutBound = "pat1out1", StudyInstanceUidOutBound = "sudIdOut1" }; + var ea2 = new ExternalAppDetails { StudyInstanceUid = "2", ExportTaskID = "ExportTaskID2", CorrelationId = "CorrelationId2", WorkflowInstanceId = "WorkflowInstanceId2", PatientIdOutBound = "pat1out2", StudyInstanceUidOutBound = "sudIdOut2" }; + + collection.InsertOne(ea1); + collection.InsertOne(ea2); + } + + public void InitDatabaseWithInferenceRequests() + { + var collection = Database.GetCollection(nameof(InferenceRequest)); + Clear(collection); + } + + internal void InitDatabaseWithDicomAssociationInfoEntries() + { + var collection = Database.GetCollection(nameof(DicomAssociationInfo)); + Clear(collection); + + var da1 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da2 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da3 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da4 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + var da5 = new DicomAssociationInfo { CalledAeTitle = Guid.NewGuid().ToString(), CallingAeTitle = Guid.NewGuid().ToString(), CorrelationId = Guid.NewGuid().ToString(), RemoteHost = "host", RemotePort = 123 }; + + collection.InsertOne(da1); + collection.InsertOne(da2); + collection.InsertOne(da3); + collection.InsertOne(da4); + collection.InsertOne(da5); + } + + public static void Clear(IMongoCollection collection) where T : class + { + collection.DeleteMany(Builders.Filter.Empty); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/PayloadRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/PayloadRepositoryTest.cs new file mode 100755 index 000000000..a4cc58f51 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/PayloadRepositoryTest.cs @@ -0,0 +1,229 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using Monai.Deploy.Messaging.Events; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class PayloadRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public PayloadRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAPayload_WhenAddingToDatabase_ExpectItToBeSaved() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = DataService.DIMSE, Destination = "called", Source = "calling" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling", "called")); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling1", "called1")); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling2", "called2")); + payload.State = Payload.PayloadState.Move; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + var actual = await collection.Find(p => p.PayloadId == payload.PayloadId).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(payload.Key, actual!.Key); + Assert.Equal(payload.State, actual!.State); + Assert.Equal(payload.Count, actual!.Count); + Assert.Equal(payload.RetryCount, actual!.RetryCount); + Assert.Equal(payload.CorrelationId, actual!.CorrelationId); + Assert.Equal(payload.WorkflowInstanceId, actual!.WorkflowInstanceId); + Assert.Equal(payload.TaskId, actual!.TaskId); + Assert.Equal(payload.DataTrigger.Source, actual!.DataTrigger.Source); + Assert.Equal(payload.DataTrigger.Destination, actual!.DataTrigger.Destination); + Assert.Equal(payload.DataTrigger.DataService, actual!.DataTrigger.DataService); + Assert.Equal(payload.Timeout, actual!.Timeout); + actual!.Files.Should().BeEquivalentTo(payload.Files, options => options.Excluding(p => p.DateReceived)); + + Assert.Equal(payload.DataTrigger, actual.Files[0].DataOrigin); + Assert.Collection(payload.DataOrigins, + item => item.Equals(actual.Files[1]), + item => item.Equals(actual.Files[2])); + } + + [Fact] + public async Task GivenAPayload_WhenRemoveIsCalled_ExpectItToDeleted() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "calling", "called")); + payload.State = Payload.PayloadState.Move; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var added = await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var removed = await store.RemoveAsync(added!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(removed, added); + + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + var dbResult = await collection.Find(p => p.PayloadId == payload.PayloadId).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated).Excluding(p => p.Elapsed).Excluding(p => p.HasTimedOut)); + } + + [Fact] + public async Task GivenAPayload_WhenUpdateIsCalled_ExpectItToSaved() + { + var payload = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = DataService.DIMSE, Destination = "dest", Source = "source" }, 5); + payload.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.DIMSE, "source", "dest")); + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + var added = await store.AddAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + added.State = Payload.PayloadState.Notify; + added.Add(new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), DataService.ACR, "calling", "called")); + var updated = await store.UpdateAsync(payload).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(updated); + + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + var actual = await collection.Find(p => p.PayloadId == payload.PayloadId).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(updated.Key, actual!.Key); + Assert.Equal(updated.State, actual!.State); + Assert.Equal(updated.Count, actual!.Count); + Assert.Equal(updated.RetryCount, actual!.RetryCount); + Assert.Equal(updated.CorrelationId, actual!.CorrelationId); + Assert.Equal(updated.WorkflowInstanceId, actual!.WorkflowInstanceId); + Assert.Equal(updated.TaskId, actual!.TaskId); + Assert.Equal(updated.DataTrigger.Source, actual!.DataTrigger.Source); + Assert.Equal(updated.DataTrigger.Destination, actual!.DataTrigger.Destination); + Assert.Equal(updated.DataTrigger.DataService, actual!.DataTrigger.DataService); + Assert.Equal(updated.Timeout, actual!.Timeout); + actual!.Files.Should().BeEquivalentTo(payload.Files, options => options.Excluding(p => p.DateReceived)); + + Assert.Equal(updated.DataTrigger, actual.Files[0].DataOrigin); + + Assert.Collection(updated.DataOrigins, + item => item.Equals(actual.Files[1].DataOrigin)); + } + + [Fact] + public async Task GivenPayloadsInDifferentStates_WhenRemovePendingPayloadsAsyncIsCalled_ExpectPendingPayloadsToBeRemoved() + { + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + MongoDatabaseFixture.Clear(collection); + + var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Move }; + var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + _ = await store.AddAsync(payload1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload4).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload5).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.RemovePendingPayloadsAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result); + + var actual = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(3, actual.Count); + + foreach (var payload in actual) + { + Assert.NotEqual(Payload.PayloadState.Created, payload.State); + } + } + + [Fact] + public async Task GivenPayloadsInDifferentStates_WhenGetPayloadsInStateAsyncIsCalled_ExpectMatchingPayloadsToBeReturned() + { + var collection = _databaseFixture.Database.GetCollection(nameof(Payload)); + MongoDatabaseFixture.Clear(collection); + + var payload1 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload2 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Created }; + var payload3 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Move }; + var payload4 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + var payload5 = new Payload(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 5) { State = Payload.PayloadState.Notify }; + + var store = new PayloadRepository(_serviceScopeFactory.Object, _logger.Object, _options); + _ = await store.AddAsync(payload1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload3).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload4).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _ = await store.AddAsync(payload5).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Move).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Single(result); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Created).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result.Count); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(2, result.Count); + + result = await store.GetPayloadsInStateAsync(CancellationToken.None, Payload.PayloadState.Notify, Payload.PayloadState.Created).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(4, result.Count); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/SourceApplicationEntityRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/SourceApplicationEntityRepositoryTest.cs new file mode 100755 index 000000000..feabc8db7 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/SourceApplicationEntityRepositoryTest.cs @@ -0,0 +1,176 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class SourceApplicationEntityRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public SourceApplicationEntityRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithSourceApplicationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenASourceApplicationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new SourceApplicationEntity + { + AeTitle = "AET", + Name = "AET", + HostIp = "localhost" + }; + + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(SourceApplicationEntity)); + var actual = await collection.Find(p => p.Name == aet.Name).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.AeTitle, actual!.AeTitle); + Assert.Equal(aet.Name, actual!.Name); + Assert.Equal(aet.HostIp, actual!.HostIp); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var result = await store.ContainsAsync(p => p.AeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.AeTitle.Equals("AET1")).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.AeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAETitle_WhenFindByAETitleAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByAETAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual.FirstOrDefault()!.AeTitle); + Assert.Equal("AET1", actual.FirstOrDefault()!.Name); + + actual = await store.FindByAETAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Empty(actual); + } + + [Fact] + public async Task GivenASourceApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(SourceApplicationEntity)); + var dbResult = await collection.Find(p => p.Name == "AET5").FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var collection = _databaseFixture.Database.GetCollection(nameof(SourceApplicationEntity)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenASourceApplicationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new SourceApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.AeTitle = "AET100"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByNameAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.AeTitle, dbResult!.AeTitle); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/StorageMetadataWrapperRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/StorageMetadataWrapperRepositoryTest.cs new file mode 100755 index 000000000..0f7fdae35 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/StorageMetadataWrapperRepositoryTest.cs @@ -0,0 +1,279 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using Monai.Deploy.Messaging.Events; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class StorageMetadataWrapperRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public StorageMetadataWrapperRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenStorageMetadataWrapperRepositoryType_WhenInitialized_TheConstructorShallGuardAllParameters() + { + Assert.Throws(() => new StorageMetadataWrapperRepository(null!, null!, null!)); + Assert.Throws(() => new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, null!, null!)); + Assert.Throws(() => new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, null!)); + + _ = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + } + + [Fact] + public async Task GivenADicomStorageMetadataObject_WhenAddingToDatabase_ExpectItToBeSaved() + { + var metadata = CreateMetadataObject(); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(StorageMetadataWrapper)); + var actual = await collection.Find(p => p.Identity == metadata.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(metadata.CorrelationId, actual!.CorrelationId); + Assert.Equal(metadata.IsUploaded, actual!.IsUploaded); + Assert.Equal(metadata.GetType().AssemblyQualifiedName, actual!.TypeName); + Assert.Equal(JsonSerializer.Serialize(metadata), actual!.Value); + } + + [Fact] + public async Task GivenANonExistedDicomStorageMetadataObject_WhenSavedToDatabase_ThrowsArgumentException() + { + var metadata1 = CreateMetadataObject(); + var metadata2 = CreateMetadataObject(); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + await store.AddOrUpdateAsync(metadata1).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await Assert.ThrowsAsync(async () => await store.UpdateAsync(metadata2).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + } + + [Fact] + public async Task GivenAnExistingDicomStorageMetadataObject_WhenUpdated_ExpectItToBeSaved() + { + var metadata = CreateMetadataObject(); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + metadata.SetWorkflows("A", "B", "C"); + metadata.File.SetUploaded("bucket"); + + await store.AddOrUpdateAsync(metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(StorageMetadataWrapper)); + var actual = await collection.Find(p => p.Identity == metadata.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(metadata.CorrelationId, actual!.CorrelationId); + Assert.Equal(metadata.IsUploaded, actual!.IsUploaded); + Assert.Equal(metadata.GetType().AssemblyQualifiedName, actual!.TypeName); + Assert.Equal(JsonSerializer.Serialize(metadata), actual!.Value); + + var unwrapped = actual.GetObject(); + Assert.NotNull(unwrapped); + + Assert.Equal(metadata.Workflows, unwrapped!.Workflows); + Assert.Equal(metadata.File.TemporaryBucketName, unwrapped!.File.TemporaryBucketName); + } + + [Fact] + public async Task GivenACorrelationId_WhenGetFileStorageMetdadataIsCalled_ExpectMatchingFileStorageMetadataToBeReturned() + { + var correlationId = Guid.NewGuid(); + var list = new List{ + CreateMetadataObject(correlationId), + CreateMetadataObject(correlationId), + CreateMetadataObject(), + new FhirFileStorageMetadata( + correlationId.ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + FhirStorageFormat.Json, + DataService.ACR, + "origin"), + new FhirFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + FhirStorageFormat.Json, + DataService.ACR, + "origin"), + }; + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + foreach (var item in list) + { + await store.AddOrUpdateAsync(item).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + } + + var results = await store.GetFileStorageMetdadataAsync(correlationId.ToString()).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Equal(3, results.Count); + + Assert.Collection(results, + (item) => item!.Id.Equals(list[0].Id), + (item) => item!.Id.Equals(list[1].Id), + (item) => item!.Id.Equals(list[2].Id)); + } + + [Fact] + public async Task GivenACorrelationIdAndAnIdentity_WhenGetFileStorageMetadadataIsCalled_ExpectMatchingFileStorageMetadataToBeReturned() + { + var correlationId = Guid.NewGuid().ToString(); + var identifier = Guid.NewGuid().ToString(); + var expected = new DicomFileStorageMetadata( + correlationId, + identifier, + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + DataService.DIMSE, + "calling", + "called"); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddOrUpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var match = await store.GetFileStorageMetdadataAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(match); + Assert.Equal(expected.Id, match!.Id); + Assert.Equal(expected.CorrelationId, match.CorrelationId); + } + + [Fact] + public async Task GivenACorrelationIdAndAnIdentity_WhenDeleteAsyncIsCalled_ExpectMatchingInstanceToBeDeleted() + { + var correlationId = Guid.NewGuid().ToString(); + var identifier = Guid.NewGuid().ToString(); + var expected = new DicomFileStorageMetadata( + correlationId, + identifier, + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + DataService.DIMSE, + "calling", + "called"); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + + var collection = _databaseFixture.Database.GetCollection(nameof(StorageMetadataWrapper)); + var stored = await collection.Find(p => p.Identity == identifier).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.Null(stored); + } + + [Fact] + public async Task GivenACorrelationIdAndAnIdentity_WhenDeleteAsyncIsCalledWithoutAMatch_ExpectNothingIsDeleted() + { + var correlationId = Guid.NewGuid().ToString(); + var identifier = Guid.NewGuid().ToString(); + var pending = CreateMetadataObject(); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(pending).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await store.DeleteAsync(correlationId, identifier).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + + var collection = _databaseFixture.Database.GetCollection(nameof(StorageMetadataWrapper)); + var stored = await collection.Find(p => p.Identity == pending.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(stored); + } + + [Fact] + public async Task GivenStorageMetadataObjects_WhenDeletingPendingUploadsObject_ExpectAllPendingObjectsToBeDeleted() + { + var collection = _databaseFixture.Database.GetCollection(nameof(StorageMetadataWrapper)); + MongoDatabaseFixture.Clear(collection); + + var pending = CreateMetadataObject(); + var uploaded = CreateMetadataObject(); + uploaded.File.SetUploaded("bucket"); + uploaded.JsonFile.SetUploaded("bucket"); + + var store = new StorageMetadataWrapperRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(pending).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await store.AddAsync(uploaded).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + await store.DeletePendingUploadsAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var result = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Single(result); + Assert.Equal(uploaded.Id, result[0].Identity); + } + + private static DicomFileStorageMetadata CreateMetadataObject() => CreateMetadataObject(Guid.NewGuid()); + + private static DicomFileStorageMetadata CreateMetadataObject(Guid corrleationId) => new( + corrleationId.ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "calling", + "called"); + } +} diff --git a/src/Database/MongoDB/Configurations/MongoDBOptions.cs b/src/Database/MongoDB/Integration.Test/Usings.cs similarity index 67% rename from src/Database/MongoDB/Configurations/MongoDBOptions.cs rename to src/Database/MongoDB/Integration.Test/Usings.cs index 34a49ef7e..1a1f3f8a0 100644 --- a/src/Database/MongoDB/Configurations/MongoDBOptions.cs +++ b/src/Database/MongoDB/Integration.Test/Usings.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright 2022 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,13 +14,4 @@ * limitations under the License. */ -using Microsoft.Extensions.Configuration; - -namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations -{ - public class MongoDBOptions - { - [ConfigurationKeyName("DatabaseName")] - public string DaatabaseName { get; set; } = string.Empty; - } -} +global using Xunit; diff --git a/src/Database/MongoDB/Integration.Test/VirtualApplicationEntityRepositoryTest.cs b/src/Database/MongoDB/Integration.Test/VirtualApplicationEntityRepositoryTest.cs new file mode 100755 index 000000000..4deba451e --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/VirtualApplicationEntityRepositoryTest.cs @@ -0,0 +1,177 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test; +using Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories; +using MongoDB.Driver; +using Moq; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test +{ + [Collection("MongoDatabase")] + public class VirtualApplicationEntityRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public VirtualApplicationEntityRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithVirtualApplicationEntities(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenAddingToDatabase_ExpectItToBeSaved() + { + var aet = new VirtualApplicationEntity + { + VirtualAeTitle = "AET", + Name = "AET", + Workflows = new List { "W1", "W2" }, + PlugInAssemblies = new List { "AssemblyA", "AssemblyB", "AssemblyC" }, + }; + + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(aet).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(VirtualApplicationEntity)); + var actual = await collection.Find(p => p.Name == aet.Name).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(aet.VirtualAeTitle, actual!.VirtualAeTitle); + Assert.Equal(aet.Name, actual!.Name); + Assert.Equal(aet.Workflows, actual!.Workflows); + Assert.Equal(aet.PlugInAssemblies, actual!.PlugInAssemblies); + } + + [Fact] + public async Task GivenAExpressionFilter_WhenContainsAsyncIsCalled_ExpectItToReturnMatchingObjects() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var result = await store.ContainsAsync(p => p.VirtualAeTitle == "AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.VirtualAeTitle.Equals("AET1")).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name != "AET2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.True(result); + result = await store.ContainsAsync(p => p.Name == "AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.False(result); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByNameAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByNameAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.VirtualAeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByNameAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAAETitleName_WhenFindByVirtualAeTitleAsyncIsCalled_ExpectItToReturnMatchingEntity() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var actual = await store.FindByAeTitleAsync("AET1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal("AET1", actual!.VirtualAeTitle); + Assert.Equal("AET1", actual!.Name); + + actual = await store.FindByAeTitleAsync("AET6").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(actual); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByAeTitleAsync("AET5").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(VirtualApplicationEntity)); + var dbResult = await collection.Find(p => p.Name == "AET5").FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenDestinationApplicationEntitiesInTheDatabase_WhenToListIsCalled_ExpectAllEntitiesToBeReturned() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var collection = _databaseFixture.Database.GetCollection(nameof(VirtualApplicationEntity)); + var expected = await collection.Find(Builders.Filter.Empty).ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await store.ToListAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + actual.Should().BeEquivalentTo(expected, options => options.Excluding(p => p.DateTimeCreated)); + } + + [Fact] + public async Task GivenAVirtualApplicationEntity_WhenUpdatedIsCalled_ExpectItToSaved() + { + var store = new VirtualApplicationEntityRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = await store.FindByAeTitleAsync("AET3").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + expected!.VirtualAeTitle = "AET100"; + + var actual = await store.UpdateAsync(expected).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Equal(expected, actual); + + var dbResult = await store.FindByAeTitleAsync("AET100").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(dbResult); + Assert.Equal(expected.VirtualAeTitle, dbResult!.VirtualAeTitle); + } + } +} diff --git a/src/Database/MongoDB/Integration.Test/packages.lock.json b/src/Database/MongoDB/Integration.Test/packages.lock.json new file mode 100644 index 000000000..6dad98917 --- /dev/null +++ b/src/Database/MongoDB/Integration.Test/packages.lock.json @@ -0,0 +1,591 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "FluentAssertions": { + "type": "Direct", + "requested": "[6.12.2, )", + "resolved": "6.12.2", + "contentHash": "8YE+xJmT8wgzEpFuzJ4S62oFhEL/AKouMz1RWPEMEUhy9H11aRQlGIWcHurH5BEy7tbF6gb0CJrs0wOw/AtDcQ==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.4.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", + "dependencies": { + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", + "dependencies": { + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" + }, + "Ardalis.GuardClauses": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", + "dependencies": { + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "fo-dicom": { + "type": "Transitive", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" + } + }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Macross.Json.Extensions": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.13.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Monai.Deploy.Messaging": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" + } + }, + "Monai.Deploy.Storage": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Storage.S3Policy": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "Polly": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "4.4.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog==" + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" + }, + "TestableIO.System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "monai.deploy.informaticsgateway.api": { + "type": "Project", + "dependencies": { + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.common": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" + } + }, + "monai.deploy.informaticsgateway.configuration": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" + } + }, + "monai.deploy.informaticsgateway.database.api": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" + } + }, + "monai.deploy.informaticsgateway.database.mongodb": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/Database/MongoDB/Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj b/src/Database/MongoDB/Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj index 56f0a462a..0427e5dfc 100644 --- a/src/Database/MongoDB/Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj +++ b/src/Database/MongoDB/Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj @@ -13,35 +13,33 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - Monai.Deploy.InformaticsGateway.Database.MongoDB - net6.0 + net8.0 Apache-2.0 enable ..\..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset enable true + false - - - - + + + - - - - + + + + + - - + \ No newline at end of file diff --git a/src/Database/MongoDB/MongoDatabaseMigrationManager.cs b/src/Database/MongoDB/MongoDatabaseMigrationManager.cs old mode 100644 new mode 100755 index f373d3e80..151282bad --- a/src/Database/MongoDB/MongoDatabaseMigrationManager.cs +++ b/src/Database/MongoDB/MongoDatabaseMigrationManager.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,9 @@ using Microsoft.Extensions.Hosting; using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; namespace Monai.Deploy.InformaticsGateway.Database.MongoDB { @@ -24,6 +27,10 @@ public class MongoDatabaseMigrationManager : IDatabaseMigrationManager { public IHost Migrate(IHost host) { +#pragma warning disable 618 + BsonDefaults.GuidRepresentationMode = GuidRepresentationMode.V3; + BsonSerializer.RegisterSerializer(typeof(Guid), new GuidSerializer(GuidRepresentation.Standard)); +#pragma warning restore MonaiApplicationEntityConfiguration.Configure(); MongoDBEntityBaseConfiguration.Configure(); DestinationApplicationEntityConfiguration.Configure(); @@ -31,6 +38,9 @@ public IHost Migrate(IHost host) InferenceRequestConfiguration.Configure(); PayloadConfiguration.Configure(); StorageMetadataWrapperEntityConfiguration.Configure(); + DicomAssociationInfoConfiguration.Configure(); + VirtualApplicationEntityConfiguration.Configure(); + ExternalAppDetailsConfiguration.Configure(); return host; } } diff --git a/src/Database/MongoDB/Repositories/DestinationApplicationEntityRepository.cs b/src/Database/MongoDB/Repositories/DestinationApplicationEntityRepository.cs old mode 100644 new mode 100755 index 0807986b6..85c071e24 --- a/src/Database/MongoDB/Repositories/DestinationApplicationEntityRepository.cs +++ b/src/Database/MongoDB/Repositories/DestinationApplicationEntityRepository.cs @@ -19,12 +19,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -42,22 +41,22 @@ public class DestinationApplicationEntityRepository : IDestinationApplicationEnt public DestinationApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) + IOptions options, + IOptions mongoDbOptions) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); - Guard.Against.Null(mongoDbOptions); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + Guard.Against.Null(mongoDbOptions, nameof(mongoDbOptions)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(DestinationApplicationEntity)); CreateIndexes(); } @@ -88,7 +87,7 @@ public async Task> ToListAsync(CancellationTo public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -100,7 +99,7 @@ public async Task> ToListAsync(CancellationTo public async Task AddAsync(DestinationApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -111,7 +110,7 @@ public async Task AddAsync(DestinationApplicationE public async Task UpdateAsync(DestinationApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -126,7 +125,7 @@ public async Task UpdateAsync(DestinationApplicati public async Task RemoveAsync(DestinationApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/MongoDB/Repositories/DicomAssociationInfoRepository.cs b/src/Database/MongoDB/Repositories/DicomAssociationInfoRepository.cs new file mode 100755 index 000000000..1056bc0c1 --- /dev/null +++ b/src/Database/MongoDB/Repositories/DicomAssociationInfoRepository.cs @@ -0,0 +1,121 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public class DicomAssociationInfoRepository : MongoDBRepositoryBase, IDicomAssociationInfoRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public DicomAssociationInfoRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(DicomAssociationInfo)); + } + + public async Task AddAsync(DicomAssociationInfo item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(item, cancellationToken: cancellationToken).ConfigureAwait(false); + return item; + }).ConfigureAwait(false); + } + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection.Find(Builders.Filter.Empty).ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public Task> GetAllAsync(int skip, + int? limit, + DateTime startTime, + DateTime endTime, + CancellationToken cancellationToken) + { + var builder = Builders.Filter; + var filter = builder.Empty; + filter &= builder.Where(t => t.DateTimeDisconnected >= startTime.ToUniversalTime()); + filter &= builder.Where(t => t.DateTimeDisconnected <= endTime.ToUniversalTime()); + + return GetAllAsync(_collection, + filter, + Builders.Sort.Descending(x => x.DateTimeDisconnected), + skip, + limit); + } + + public Task CountAsync() + { + return _collection.CountDocumentsAsync(Builders.Filter.Empty); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/MongoDB/Repositories/ExternalAppDetailsRepository.cs b/src/Database/MongoDB/Repositories/ExternalAppDetailsRepository.cs new file mode 100755 index 000000000..16a4a7feb --- /dev/null +++ b/src/Database/MongoDB/Repositories/ExternalAppDetailsRepository.cs @@ -0,0 +1,139 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public class ExternalAppDetailsRepository : IExternalAppDetailsRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public ExternalAppDetailsRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(ExternalAppDetails)); + CreateIndexes(); + } + + private void CreateIndexes() + { + var indexDefinitionState = Builders.IndexKeys + .Ascending(_ => _.StudyInstanceUid); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState)); + + indexDefinitionState = Builders.IndexKeys + .Ascending(_ => _.PatientIdOutBound); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState)); + + indexDefinitionState = Builders.IndexKeys + .Ascending(_ => _.StudyInstanceUidOutBound); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState)); + + var options = new CreateIndexOptions { ExpireAfter = TimeSpan.FromDays(7), Name = "DateTimeCreated" }; + indexDefinitionState = Builders.IndexKeys.Ascending(_ => _.DateTimeCreated); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState, options)); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + public async Task AddAsync(ExternalAppDetails details, CancellationToken cancellationToken) + { + Guard.Against.Null(details, nameof(details)); + + await _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(details, cancellationToken: cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task> GetAsync(string studyInstanceId, CancellationToken cancellationToken) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return (await _collection.FindAsync(p => + p.StudyInstanceUid == studyInstanceId, null, cancellationToken + ).ConfigureAwait(false)).ToList(); + }).ConfigureAwait(false); + } + + public async Task GetByPatientIdOutboundAsync(string patientId, CancellationToken cancellationToken) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return (await _collection.FindAsync(p => + p.PatientIdOutBound == patientId, null, cancellationToken + ).ConfigureAwait(false)).FirstOrDefault(); + }).ConfigureAwait(false); + } + + public async Task GetByStudyIdOutboundAsync(string studyInstanceId, CancellationToken cancellationToken) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return (await _collection.FindAsync(p => + p.StudyInstanceUidOutBound == studyInstanceId, null, cancellationToken + ).ConfigureAwait(false)).FirstOrDefault(); + }).ConfigureAwait(false); + } + } +} diff --git a/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs b/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs new file mode 100755 index 000000000..de51c885f --- /dev/null +++ b/src/Database/MongoDB/Repositories/HL7DestinationEntityRepository.cs @@ -0,0 +1,172 @@ +/* + * Copyright 2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public class HL7DestinationEntityRepository : IHL7DestinationEntityRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public HL7DestinationEntityRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options, + IOptions mongoDbOptions) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + Guard.Against.Null(mongoDbOptions, nameof(mongoDbOptions)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => + { + _logger.DatabaseErrorRetry(timespan, count, exception); + }); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(HL7DestinationEntity)); + CreateIndexes(); + } + + private void CreateIndexes() + { + var options = new CreateIndexOptions { Unique = true }; + + var indexDefinition = Builders.IndexKeys + .Ascending(_ => _.Name); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinition, options)); + + var indexDefinitionAll = Builders.IndexKeys.Combine( + Builders.IndexKeys.Ascending(_ => _.Name), + Builders.IndexKeys.Ascending(_ => _.HostIp), + Builders.IndexKeys.Ascending(_ => _.Port)); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionAll, options)); + } + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection.Find(Builders.Filter.Empty).ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection + .Find(x => x.Name == name) + .FirstOrDefaultAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task AddAsync(HL7DestinationEntity item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(item, cancellationToken: cancellationToken).ConfigureAwait(false); + return item; + }).ConfigureAwait(false); + } + + public async Task UpdateAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.ReplaceOneAsync(p => p.Id == entity.Id, entity, cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.ModifiedCount == 0) + { + throw new DatabaseException("Failed to update entity"); + } + return entity; + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(HL7DestinationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.DeleteOneAsync(Builders.Filter.Where(p => p.Name == entity.Name), cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.DeletedCount == 0) + { + throw new DatabaseException("Failed to delete entity"); + } + return entity; + }).ConfigureAwait(false); + } + + public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.FindAsync(predicate, cancellationToken: cancellationToken).ConfigureAwait(false); + return await result.AnyAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs b/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs new file mode 100755 index 000000000..8b23cf257 --- /dev/null +++ b/src/Database/MongoDB/Repositories/Hl7ApplicationConfigRepository.cs @@ -0,0 +1,152 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public class Hl7ApplicationConfigRepository : IHl7ApplicationConfigRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public Hl7ApplicationConfigRepository(IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => + { + _logger.DatabaseErrorRetry(timespan, count, exception); + }); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(Hl7ApplicationConfigEntity)); + CreateIndexes(); + } + + private void CreateIndexes() + { + var options = new CreateIndexOptions { Unique = true }; + + var indexDefinition = Builders.IndexKeys + .Ascending(_ => _.DateTimeCreated); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinition, options)); + } + + public Task> GetAllAsync(CancellationToken cancellationToken = default) => + _retryPolicy.ExecuteAsync(() => + _collection.Find(Builders.Filter.Empty).ToListAsync(cancellationToken)); + + public Task GetByIdAsync(string id) + { + var GuidId = Guid.Parse(id); + return _retryPolicy.ExecuteAsync(() => _collection + .Find(x => x.Id == GuidId) + .FirstOrDefaultAsync())!; + } + + public Task DeleteAsync(string id, CancellationToken cancellationToken = default) + { + return _retryPolicy.ExecuteAsync(async () => + { + var entity = await GetByIdAsync(id).ConfigureAwait(false) ?? throw new DatabaseException("Failed to delete entity."); + + var result = await _collection + .DeleteOneAsync(Builders.Filter.Where(p => p.Id == entity.Id), + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (result.DeletedCount == 0) + { + throw new DatabaseException("Failed to delete entity"); + } + + return entity; + }); + } + + public Task CreateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default) + { + return _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(configEntity, cancellationToken: cancellationToken) + .ConfigureAwait(false); + return configEntity; + }); + } + + public Task UpdateAsync(Hl7ApplicationConfigEntity configEntity, + CancellationToken cancellationToken = default) + { + + return _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection + .ReplaceOneAsync(Builders.Filter.Where(p => p.Id == configEntity.Id), + configEntity, cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.ModifiedCount == 0) + { + throw new DatabaseException("Failed to update entity"); + } + + return configEntity; + })!; + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/MongoDB/Repositories/InferenceRequestRepository.cs b/src/Database/MongoDB/Repositories/InferenceRequestRepository.cs old mode 100644 new mode 100755 index d064eb93b..ecfb1a71c --- a/src/Database/MongoDB/Repositories/InferenceRequestRepository.cs +++ b/src/Database/MongoDB/Repositories/InferenceRequestRepository.cs @@ -16,7 +16,6 @@ */ using Ardalis.GuardClauses; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -25,7 +24,6 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -35,7 +33,6 @@ namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories public class InferenceRequestRepository : InferenceRequestRepositoryBase, IDisposable { private readonly ILogger _logger; - private readonly IOptions _options; private readonly IServiceScope _scope; private readonly IMongoCollection _collection; private readonly AsyncRetryPolicy _retryPolicy; @@ -46,20 +43,18 @@ public class InferenceRequestRepository : InferenceRequestRepositoryBase, IDispo public InferenceRequestRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) : base(logger, options) + IOptions options) : base(logger, options) { - Guard.Against.Null(serviceScopeFactory); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _options = options ?? throw new ArgumentNullException(nameof(options)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(InferenceRequest)); CreateIndexes(); } @@ -79,7 +74,7 @@ private void CreateIndexes() public override async Task AddAsync(InferenceRequest inferenceRequest, CancellationToken cancellationToken = default) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "TransactionId", inferenceRequest.TransactionId } }); await _retryPolicy.ExecuteAsync(async () => @@ -123,7 +118,7 @@ public override async Task TakeAsync(CancellationToken cancell public override async Task GetInferenceRequestAsync(string transactionId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(transactionId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -135,7 +130,7 @@ public override async Task TakeAsync(CancellationToken cancell public override async Task GetInferenceRequestAsync(Guid inferenceRequestId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrEmpty(inferenceRequestId); + Guard.Against.NullOrEmpty(inferenceRequestId, nameof(inferenceRequestId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -147,7 +142,7 @@ public override async Task TakeAsync(CancellationToken cancell protected override async Task SaveAsync(InferenceRequest inferenceRequest, CancellationToken cancellationToken = default) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/MongoDB/Repositories/MonaiApplicationEntityRepository.cs b/src/Database/MongoDB/Repositories/MonaiApplicationEntityRepository.cs old mode 100644 new mode 100755 index f160457a1..0f9ecdb36 --- a/src/Database/MongoDB/Repositories/MonaiApplicationEntityRepository.cs +++ b/src/Database/MongoDB/Repositories/MonaiApplicationEntityRepository.cs @@ -19,12 +19,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -42,22 +41,20 @@ public class MonaiApplicationEntityRepository : IMonaiApplicationEntityRepositor public MonaiApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) + IOptions options) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); - Guard.Against.Null(mongoDbOptions); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(MonaiApplicationEntity)); CreateIndexes(); } @@ -81,7 +78,7 @@ public async Task> ToListAsync(CancellationToken ca public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -93,7 +90,7 @@ public async Task> ToListAsync(CancellationToken ca public async Task AddAsync(MonaiApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -104,7 +101,7 @@ public async Task AddAsync(MonaiApplicationEntity item, public async Task UpdateAsync(MonaiApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -119,7 +116,7 @@ public async Task UpdateAsync(MonaiApplicationEntity ent public async Task RemoveAsync(MonaiApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/MongoDB/Repositories/MongoDBRepositoryBase.cs b/src/Database/MongoDB/Repositories/MongoDBRepositoryBase.cs new file mode 100644 index 000000000..06f81a9d0 --- /dev/null +++ b/src/Database/MongoDB/Repositories/MongoDBRepositoryBase.cs @@ -0,0 +1,64 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using MongoDB.Driver; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public abstract class MongoDBRepositoryBase + { + /// + /// Get All T that match filters provided. + /// + /// + /// Collection to run against. + /// Filter function you can filter on properties of T. + /// Function used to sort data. + /// Items to skip. + /// Items to limit results by. + /// + protected static async Task> GetAllAsync(IMongoCollection collection, + Expression>? filterFunction, + SortDefinition sortFunction, + int? skip = null, + int? limit = null) + { + return await collection + .Find(filterFunction) + .Skip(skip) + .Limit(limit) + .Sort(sortFunction) + .ToListAsync().ConfigureAwait(false); + } + + protected static async Task> GetAllAsync(IMongoCollection collection, + FilterDefinition filterFunction, + SortDefinition sortFunction, + int? skip = null, + int? limit = null) + { + var result = await collection + .Find(filterFunction) + .Skip(skip) + .Limit(limit) + .Sort(sortFunction) + .ToListAsync().ConfigureAwait(false); + return result; + } + } +} diff --git a/src/Database/MongoDB/Repositories/PayloadRepository.cs b/src/Database/MongoDB/Repositories/PayloadRepository.cs old mode 100644 new mode 100755 index 6f84784f2..463e5f744 --- a/src/Database/MongoDB/Repositories/PayloadRepository.cs +++ b/src/Database/MongoDB/Repositories/PayloadRepository.cs @@ -14,9 +14,7 @@ * limitations under the License. */ -using System.Linq.Expressions; using Ardalis.GuardClauses; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -25,7 +23,6 @@ using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -43,44 +40,35 @@ public class PayloadRepository : IPayloadRepository, IDisposable public PayloadRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) + IOptions options + ) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); - Guard.Against.Null(mongoDbOptions); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(Payload)); CreateIndexes(); } private void CreateIndexes() { - var options = new CreateIndexOptions { Unique = true }; - var indexDefinitionState = Builders.IndexKeys .Ascending(_ => _.State); _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState)); - - var indexDefinition = Builders.IndexKeys.Combine( - Builders.IndexKeys.Ascending(_ => _.CorrelationId), - Builders.IndexKeys.Ascending(_ => _.PayloadId)); - - _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinition, options)); } public async Task AddAsync(Payload item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -89,18 +77,9 @@ public async Task AddAsync(Payload item, CancellationToken cancellation }).ConfigureAwait(false); } - public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) - { - return await _retryPolicy.ExecuteAsync(async () => - { - var result = await _collection.FindAsync(predicate, cancellationToken: cancellationToken).ConfigureAwait(false); - return await result.AnyAsync(cancellationToken).ConfigureAwait(false); - }).ConfigureAwait(false); - } - public async Task RemoveAsync(Payload entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -123,7 +102,7 @@ public async Task> ToListAsync(CancellationToken cancellationToken public async Task UpdateAsync(Payload entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -140,7 +119,7 @@ public async Task RemovePendingPayloadsAsync(CancellationToken cancellation { return await _retryPolicy.ExecuteAsync(async () => { - var results = await _collection.DeleteManyAsync(Builders.Filter.Where(p => p.State == Payload.PayloadState.Created), cancellationToken).ConfigureAwait(false); + var results = await _collection.DeleteManyAsync(Builders.Filter.Where(p => p.State == Payload.PayloadState.Created && p.MachineName == Environment.MachineName), cancellationToken).ConfigureAwait(false); return Convert.ToInt32(results.DeletedCount); }).ConfigureAwait(false); } diff --git a/src/Database/MongoDB/Repositories/SourceApplicationEntityRepository.cs b/src/Database/MongoDB/Repositories/SourceApplicationEntityRepository.cs old mode 100644 new mode 100755 index 4d179a554..2adc525b2 --- a/src/Database/MongoDB/Repositories/SourceApplicationEntityRepository.cs +++ b/src/Database/MongoDB/Repositories/SourceApplicationEntityRepository.cs @@ -24,7 +24,6 @@ using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -42,22 +41,21 @@ public class SourceApplicationEntityRepository : ISourceApplicationEntityReposit public SourceApplicationEntityRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) + IOptions options + ) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); - Guard.Against.Null(mongoDbOptions); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle().WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(SourceApplicationEntity)); CreateIndexes(); } @@ -87,7 +85,7 @@ public async Task> ToListAsync(CancellationToken c public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(name); + Guard.Against.NullOrWhiteSpace(name, nameof(name)); return await _retryPolicy.ExecuteAsync(async () => { @@ -97,9 +95,19 @@ public async Task> ToListAsync(CancellationToken c }).ConfigureAwait(false); } + public async Task FindByAETAsync(string aeTitle, CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return (await _collection + .Find(x => x.AeTitle == aeTitle) + .ToListAsync(cancellationToken).ConfigureAwait(false)).ToArray(); + }).ConfigureAwait(false); + } + public async Task AddAsync(SourceApplicationEntity item, CancellationToken cancellationToken = default) { - Guard.Against.Null(item); + Guard.Against.Null(item, nameof(item)); return await _retryPolicy.ExecuteAsync(async () => { @@ -110,7 +118,7 @@ public async Task AddAsync(SourceApplicationEntity item public async Task UpdateAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { @@ -125,7 +133,7 @@ public async Task UpdateAsync(SourceApplicationEntity e public async Task RemoveAsync(SourceApplicationEntity entity, CancellationToken cancellationToken = default) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); return await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/MongoDB/Repositories/StorageMetadataWrapperRepository.cs b/src/Database/MongoDB/Repositories/StorageMetadataWrapperRepository.cs old mode 100644 new mode 100755 index 25d51a47f..d52d7d696 --- a/src/Database/MongoDB/Repositories/StorageMetadataWrapperRepository.cs +++ b/src/Database/MongoDB/Repositories/StorageMetadataWrapperRepository.cs @@ -17,7 +17,6 @@ using System.Data; using Ardalis.GuardClauses; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -26,7 +25,6 @@ using Monai.Deploy.InformaticsGateway.Database.Api; using Monai.Deploy.InformaticsGateway.Database.Api.Logging; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.MongoDB.Configurations; using MongoDB.Driver; using Polly; using Polly.Retry; @@ -44,22 +42,21 @@ public class StorageMetadataWrapperRepository : StorageMetadataRepositoryBase, I public StorageMetadataWrapperRepository( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options, - IOptions mongoDbOptions) : base(logger) + IOptions options + ) : base(logger) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(options); - Guard.Against.Null(mongoDbOptions); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _scope = serviceScopeFactory.CreateScope(); _retryPolicy = Policy.Handle(p => p is not ArgumentException).WaitAndRetryAsync( - options.Value.Database.Retries.RetryDelays, + options.Value.Retries.RetryDelays, (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); - var mongoDatabase = mongoDbClient.GetDatabase(mongoDbOptions.Value.DaatabaseName); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); _collection = mongoDatabase.GetCollection(nameof(StorageMetadataWrapper)); CreateIndexes(); } @@ -73,13 +70,13 @@ private void CreateIndexes() _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinition)); } - protected override async Task DeleteInternalAsync(StorageMetadataWrapper toBeDeleted, CancellationToken cancellationToken) + protected override async Task DeleteInternalAsync(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(toBeDeleted); + Guard.Against.Null(metadata, nameof(metadata)); return await _retryPolicy.ExecuteAsync(async () => { - var results = await _collection.DeleteOneAsync(Builders.Filter.Where(p => p.Identity == toBeDeleted.Identity), cancellationToken: cancellationToken).ConfigureAwait(false); + var results = await _collection.DeleteOneAsync(Builders.Filter.Where(p => p.Identity == metadata.Identity), cancellationToken: cancellationToken).ConfigureAwait(false); return results.DeletedCount == 1; }).ConfigureAwait(false); } @@ -94,7 +91,7 @@ await _retryPolicy.ExecuteAsync(async () => public override async Task> GetFileStorageMetdadataAsync(string correlationId, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -107,7 +104,7 @@ public override async Task> GetFileStorageMetdadataAs public override async Task GetFileStorageMetdadataAsync(string correlationId, string identity, CancellationToken cancellationToken = default) { - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); return await _retryPolicy.ExecuteAsync(async () => { @@ -120,7 +117,7 @@ public override async Task> GetFileStorageMetdadataAs protected override async Task UpdateInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); await _retryPolicy.ExecuteAsync(async () => { @@ -145,7 +142,7 @@ await _collection.ReplaceOneAsync( protected override async Task AddAsyncInternal(StorageMetadataWrapper metadata, CancellationToken cancellationToken = default) { - Guard.Against.Null(metadata); + Guard.Against.Null(metadata, nameof(metadata)); await _retryPolicy.ExecuteAsync(async () => { diff --git a/src/Database/MongoDB/Repositories/VirtualApplicationEntityRepository.cs b/src/Database/MongoDB/Repositories/VirtualApplicationEntityRepository.cs new file mode 100755 index 000000000..a112ea53a --- /dev/null +++ b/src/Database/MongoDB/Repositories/VirtualApplicationEntityRepository.cs @@ -0,0 +1,174 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Linq.Expressions; +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.Database.MongoDB.Repositories +{ + public class VirtualApplicationEntityRepository : IVirtualApplicationEntityRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public VirtualApplicationEntityRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options + ) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(VirtualApplicationEntity)); + CreateIndexes(); + } + + private void CreateIndexes() + { + var options = new CreateIndexOptions { Unique = true }; + + var indexDefinition = Builders.IndexKeys + .Ascending(_ => _.Name); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinition, options)); + } + + public async Task> ToListAsync(CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection.Find(Builders.Filter.Empty).ToListAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByNameAsync(string name, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(name, nameof(name)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection + .Find(x => x.Name == name) + .FirstOrDefaultAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task FindByAeTitleAsync(string aeTitle, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _collection + .Find(x => x.VirtualAeTitle == aeTitle) + .FirstOrDefaultAsync().ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task AddAsync(VirtualApplicationEntity item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(item, cancellationToken: cancellationToken).ConfigureAwait(false); + return item; + }).ConfigureAwait(false); + } + + public async Task UpdateAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.ReplaceOneAsync(p => p.Id == entity.Id, entity, cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.ModifiedCount == 0) + { + throw new DatabaseException("Failed to update entity"); + } + return entity; + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(VirtualApplicationEntity entity, CancellationToken cancellationToken = default) + { + Guard.Against.Null(entity, nameof(entity)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.DeleteOneAsync(Builders.Filter.Where(p => p.Name == entity.Name), cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.DeletedCount == 0) + { + throw new DatabaseException("Failed to delete entity"); + } + return entity; + }).ConfigureAwait(false); + } + + public async Task ContainsAsync(Expression> predicate, CancellationToken cancellationToken = default) + { + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.FindAsync(predicate, cancellationToken: cancellationToken).ConfigureAwait(false); + return await result.AnyAsync(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Database/MongoDB/packages.lock.json b/src/Database/MongoDB/packages.lock.json index 01d290d3d..83e3752e3 100644 --- a/src/Database/MongoDB/packages.lock.json +++ b/src/Database/MongoDB/packages.lock.json @@ -1,56 +1,51 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "MongoDB.Driver": { "type": "Direct", - "requested": "[2.18.0, )", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "requested": "[2.30.0, )", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, - "MongoDB.Driver.Core": { + "Polly": { "type": "Direct", - "requested": "[2.18.0, )", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "requested": "[8.5.2, )", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "DnsClient": "1.6.1", - "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", - "SharpCompress": "0.30.1", - "Snappier": "1.0.0", - "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "Polly.Core": "8.5.2" } }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -61,24 +56,26 @@ }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -87,68 +84,25 @@ }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.EntityFrameworkCore": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" - }, - "Microsoft.Extensions.Caching.Abstractions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Caching.Memory": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { @@ -162,41 +116,52 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { @@ -213,36 +178,31 @@ }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -254,63 +214,98 @@ }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Polly": { + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } }, "SharpCompress": { "type": "Transitive", @@ -327,31 +322,32 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections.Immutable": { + "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.DiagnosticSource": { + "System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.Abstractions": { + "System.Memory": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -374,75 +370,75 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "4.7.2", - "contentHash": "TcMd95wcrubm9nHvJEQs70rC0H/8omiSGGpU4FQ/ZA1URIqD4pjmFJh2Mfv1yH1eHgJDWTi2hMDXwTET+zOOyg==" + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } } } diff --git a/src/Database/packages.lock.json b/src/Database/packages.lock.json index 67a78a02e..c1004c484 100644 --- a/src/Database/packages.lock.json +++ b/src/Database/packages.lock.json @@ -1,98 +1,73 @@ { "version": 1, "dependencies": { - "net6.0": { - "GitVersion.MsBuild": { + "net8.0": { + "AspNetCore.HealthChecks.MongoDb": { "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "Microsoft.EntityFrameworkCore": { - "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "requested": "[8.1.0, )", + "resolved": "8.1.0", + "contentHash": "NuVDrj7UXBVniePh6JnuM8ryZRWdOIGOBes3owg2WQV/1NPntpWqX/DYaP6SBduHULUp8XRbwAui8qKQAW4SDA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "MongoDB.Driver": "2.28.0" } }, - "Microsoft.Extensions.Configuration": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.FileExtensions": { + "Microsoft.EntityFrameworkCore.Tools": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "nWlJaFEvT7HpLkOusyReeQPeHsl5EUIBlI5Pr05SYhkWFQ7KGLmUNdv4j1aAjrO+x42Enqr7fu7r3MXLPQqImg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.EntityFrameworkCore.Design": "8.0.14" } }, - "Microsoft.Extensions.Configuration.Json": { + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -103,24 +78,31 @@ }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Macross.Json.Extensions": { "type": "Transitive", @@ -128,211 +110,368 @@ "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { "type": "Transitive", "resolved": "1.1.1", - "contentHash": "yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==" + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.6", + "contentHash": "Ms5e5QuBAjVIuQsGumeLvkgMiOpnj6wxPvwBIoe1NfTkseWK4NZYztnhgDlpkCPkrUmJEXLv69kl349Ours30Q==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "RFdomymyuPNffl+VPk7osdxCJQ0xlGuxr28ifdfFFNUaMK0OYiJOjr6w9z3kscOM2p2gdPWNI1IFUXllEyphow==", + "dependencies": { + "Humanizer.Core": "2.8.26", + "Microsoft.EntityFrameworkCore.Relational": "6.0.25" + } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" } }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "McP+Lz/EKwvtCv48z0YImw+L1gi1gy5rHhNaNIY2CrjloV+XY8gydT8DjMR6zWeL13AFK+DioVpppwAuO1Gi1w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.2", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.10", - "contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -344,89 +483,125 @@ }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", + "dependencies": { + "System.CodeDom": "4.4.0" + } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "Polly": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } }, "SharpCompress": { "type": "Transitive", @@ -440,33 +615,32 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "System.Buffers": { @@ -474,6 +648,11 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" + }, "System.Collections.Immutable": { "type": "Transitive", "resolved": "6.0.0", @@ -482,28 +661,80 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { + "System.Composition": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition.Convention": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", + "dependencies": { + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -526,103 +757,97 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.database.mongodb": { "type": "Project", "dependencies": { - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "MongoDB.Driver": "2.18.0", - "MongoDB.Driver.Core": "2.18.0" + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" } } } diff --git a/src/DicomWebClient/API/DicomWebClientException.cs b/src/DicomWebClient/API/DicomWebClientException.cs index 8e571d4dd..35607e068 100644 --- a/src/DicomWebClient/API/DicomWebClientException.cs +++ b/src/DicomWebClient/API/DicomWebClientException.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2020 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,11 +17,9 @@ using System; using System.Net; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.API { - [Serializable] public class DicomWebClientException : Exception { public HttpStatusCode? StatusCode { get; } @@ -33,24 +31,5 @@ public DicomWebClientException(HttpStatusCode? statusCode, string responseMessag StatusCode = statusCode; ResponseMessage = responseMessage; } - - protected DicomWebClientException(SerializationInfo info, StreamingContext context) : base(info, context) - { - StatusCode = (HttpStatusCode?)info.GetValue(nameof(StatusCode), typeof(HttpStatusCode?)); - ResponseMessage = info.GetString(nameof(ResponseMessage)); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - info.AddValue(nameof(StatusCode), StatusCode, typeof(HttpStatusCode?)); - info.AddValue(nameof(ResponseMessage), ResponseMessage); - - base.GetObjectData(info, context); - } } } diff --git a/src/DicomWebClient/API/IDicomWebClient.cs b/src/DicomWebClient/API/IDicomWebClient.cs index f69e764e5..dd8442e31 100644 --- a/src/DicomWebClient/API/IDicomWebClient.cs +++ b/src/DicomWebClient/API/IDicomWebClient.cs @@ -75,8 +75,11 @@ public interface IDicomWebClient /// ServiceType to be configured /// Url prefix #pragma warning disable CA1054 + void ConfigureServicePrefix(DicomWebServiceType serviceType, string urlPrefix); + #pragma warning restore CA1054 + /// /// Configures the authentication header for the DICOMweb client. /// diff --git a/src/DicomWebClient/API/IServiceBase.cs b/src/DicomWebClient/API/IServiceBase.cs index 65193a828..2c414ced6 100644 --- a/src/DicomWebClient/API/IServiceBase.cs +++ b/src/DicomWebClient/API/IServiceBase.cs @@ -20,7 +20,9 @@ namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.API public interface IServiceBase { #pragma warning disable CA1054 + bool TryConfigureServiceUriPrefix(string uriPrefix); + #pragma warning restore CA1054 } } diff --git a/src/DicomWebClient/API/ResponseDecodeException.cs b/src/DicomWebClient/API/ResponseDecodeException.cs index d90713dda..8e3550f26 100644 --- a/src/DicomWebClient/API/ResponseDecodeException.cs +++ b/src/DicomWebClient/API/ResponseDecodeException.cs @@ -16,19 +16,13 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.API { - [Serializable] public class ResponseDecodeException : Exception { public ResponseDecodeException(string message) : base(message) { } - - protected ResponseDecodeException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/DicomWebClient/API/UnsupportedReturnTypeException.cs b/src/DicomWebClient/API/UnsupportedReturnTypeException.cs index cbfa36e1c..8a188dc8f 100644 --- a/src/DicomWebClient/API/UnsupportedReturnTypeException.cs +++ b/src/DicomWebClient/API/UnsupportedReturnTypeException.cs @@ -16,19 +16,13 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.API { - [Serializable] public class UnsupportedReturnTypeException : Exception { public UnsupportedReturnTypeException(string message) : base(message) { } - - protected UnsupportedReturnTypeException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/DicomWebClient/CLI/GlobalSuppressions.cs b/src/DicomWebClient/CLI/GlobalSuppressions.cs old mode 100644 new mode 100755 index 42acd7240..5056bc4b9 --- a/src/DicomWebClient/CLI/GlobalSuppressions.cs +++ b/src/DicomWebClient/CLI/GlobalSuppressions.cs @@ -22,3 +22,4 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Design", "CA1054:URI-like parameters should not be strings", Justification = "User input from CLI", Scope = "module")] +[assembly: SuppressMessage("Sonar Code Smell", "S2094: Remove this empty class, write its code or make it an \"interface\"", Justification = "placeholder code")] diff --git a/src/DicomWebClient/CLI/Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI.csproj b/src/DicomWebClient/CLI/Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI.csproj index c9daddab8..3c2e03791 100644 --- a/src/DicomWebClient/CLI/Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI.csproj +++ b/src/DicomWebClient/CLI/Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI.csproj @@ -1,4 +1,4 @@ - - - + Exe - net6.0 + net8.0 dicomweb-cli true true ..\..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - + + + + + + - true - - - - + \ No newline at end of file diff --git a/src/DicomWebClient/CLI/Qido.cs b/src/DicomWebClient/CLI/Qido.cs index f02b6a461..2748e39a6 100644 --- a/src/DicomWebClient/CLI/Qido.cs +++ b/src/DicomWebClient/CLI/Qido.cs @@ -87,8 +87,8 @@ private Dictionary ParseQueryString(string query) private async Task SaveJson(string outputDir, IAsyncEnumerable enumerable) { - Guard.Against.NullOrWhiteSpace(outputDir); - Guard.Against.Null(enumerable); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); + Guard.Against.Null(enumerable, nameof(enumerable)); await foreach (var item in enumerable) { @@ -98,7 +98,7 @@ private async Task SaveJson(string outputDir, IAsyncEnumerable enumerabl private void ValidateOutputDirectory(ref string outputDir) { - Guard.Against.NullOrWhiteSpace(outputDir); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); if (outputDir == ".") { @@ -112,7 +112,7 @@ private void ValidateOutputDirectory(ref string outputDir) private void ValidateOptions(string rootUrl, out Uri rootUri) { - Guard.Against.NullOrWhiteSpace(rootUrl); + Guard.Against.NullOrWhiteSpace(rootUrl, nameof(rootUri)); _logger.LogInformation("Checking arguments..."); rootUri = new Uri(rootUrl); diff --git a/src/DicomWebClient/CLI/Stow.cs b/src/DicomWebClient/CLI/Stow.cs index fa673121b..509abef66 100644 --- a/src/DicomWebClient/CLI/Stow.cs +++ b/src/DicomWebClient/CLI/Stow.cs @@ -29,7 +29,7 @@ namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI { [Command("stow", "Use stow to store DICOM instances to a remote DICOMweb server.")] - public class Stow : ConsoleAppBase + public class Stow : ConsoleAppBase, IDisposable { private readonly IDicomWebClient _dicomWebClient; private readonly ILogger _logger; @@ -132,11 +132,16 @@ private void AddValidFiles(List dicomFiles, params string[] files) private void ValidateOptions(string rootUrl, out Uri rootUri) { - Guard.Against.NullOrWhiteSpace(rootUrl); + Guard.Against.NullOrWhiteSpace(rootUrl, nameof(rootUrl)); _logger.LogInformation("Checking arguments..."); rootUri = new Uri(rootUrl); rootUri = rootUri.EnsureUriEndsWithSlash(); } + + public void Dispose() + { + _cancellationTokeSource.Dispose(); + } } } diff --git a/src/DicomWebClient/CLI/Utils.cs b/src/DicomWebClient/CLI/Utils.cs index 583903e30..96b83ecc3 100644 --- a/src/DicomWebClient/CLI/Utils.cs +++ b/src/DicomWebClient/CLI/Utils.cs @@ -19,20 +19,22 @@ using System.IO; using System.Net.Http.Headers; using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading.Tasks; using Ardalis.GuardClauses; using FellowOakDicom; using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI { internal static class Utils { + public static JsonSerializerOptions JsonWriteIndentedOption = new() { WriteIndented = true }; public static void CheckAndConfirmOverwriteOutputFilename(ILogger logger, string filename) { - Guard.Against.Null(logger); - Guard.Against.NullOrWhiteSpace(filename); + Guard.Against.Null(logger, nameof(logger)); + Guard.Against.NullOrWhiteSpace(filename, nameof(filename)); if (File.Exists(filename)) { @@ -52,8 +54,8 @@ public static void CheckAndConfirmOverwriteOutputFilename(ILogger logger, public static void CheckAndConfirmOverwriteOutput(ILogger logger, string outputDir) { - Guard.Against.Null(logger); - Guard.Against.NullOrWhiteSpace(outputDir); + Guard.Against.Null(logger, nameof(logger)); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); if (Directory.Exists(outputDir)) { @@ -78,8 +80,8 @@ public static void CheckAndConfirmOverwriteOutput(ILogger logger, string o public static AuthenticationHeaderValue GenerateFromUsernamePassword(string username, string password) { - Guard.Against.NullOrWhiteSpace(username); - Guard.Against.NullOrWhiteSpace(password); + Guard.Against.NullOrWhiteSpace(username, nameof(username)); + Guard.Against.NullOrWhiteSpace(username, nameof(username)); var authToken = Encoding.ASCII.GetBytes($"{username}:{password}"); return new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken)); @@ -93,21 +95,23 @@ public static async Task SaveFiles(ILogger logger, string outputDirectory, public static async Task SaveFiles(ILogger logger, DicomFile dicomFile, string filename) { - Guard.Against.Null(logger); - Guard.Against.Null(dicomFile); - Guard.Against.NullOrWhiteSpace(filename); + Guard.Against.Null(logger, nameof(logger)); + Guard.Against.Null(dicomFile, nameof(dicomFile)); + Guard.Against.NullOrWhiteSpace(filename, nameof(filename)); logger.LogInformation($"Saving {filename}..."); await dicomFile.SaveAsync(filename).ConfigureAwait(false); } +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously internal static async Task SaveJson(ILogger logger, string outputDir, string item, DicomTag filenameSourceTag) +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { - Guard.Against.Null(logger); - Guard.Against.NullOrWhiteSpace(outputDir); - Guard.Against.NullOrWhiteSpace(item); + Guard.Against.Null(logger, nameof(logger)); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); + Guard.Against.NullOrWhiteSpace(item, nameof(item)); - var token = JToken.Parse(item); + var token = JsonObject.Parse(item).AsObject(); var value = GetTagValueFromJson(token, filenameSourceTag); string filename; if (!string.IsNullOrWhiteSpace(value)) @@ -120,30 +124,33 @@ internal static async Task SaveJson(ILogger logger, string outputDir, string ite } var path = Path.Combine(outputDir, filename); logger.LogInformation($"Saving JSON {path}"); - await File.WriteAllTextAsync(path, token.ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8).ConfigureAwait(false); + using var fileStream = File.Create(path); + using var fileWriter = new Utf8JsonWriter(fileStream); + token.WriteTo(fileWriter, JsonWriteIndentedOption); } internal static async Task SaveJson(ILogger logger, string outputFilename, string text) { - Guard.Against.Null(logger); - Guard.Against.NullOrWhiteSpace(outputFilename); - Guard.Against.NullOrWhiteSpace(text); + Guard.Against.Null(logger, nameof(logger)); + Guard.Against.NullOrWhiteSpace(outputFilename, nameof(outputFilename)); + Guard.Against.NullOrWhiteSpace(text, nameof(text)); - var token = JToken.Parse(text); + var token = JsonNode.Parse(text); logger.LogInformation($"Saving JSON {outputFilename}..."); - await File.WriteAllTextAsync(outputFilename, token.ToString(Newtonsoft.Json.Formatting.Indented), Encoding.UTF8).ConfigureAwait(false); + await File.WriteAllTextAsync(outputFilename, token.ToString(), Encoding.UTF8).ConfigureAwait(false); } - private static string GetTagValueFromJson(JToken token, DicomTag dicomTag, string defaultValue = "unknown") +#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + private static string GetTagValueFromJson(JsonObject? token, DicomTag dicomTag, string defaultValue = "unknown") +#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. { - Guard.Against.Null(token); - Guard.Against.Null(dicomTag); + Guard.Against.Null(dicomTag, nameof(dicomTag)); var tag = $"{dicomTag.Group:X4}{dicomTag.Element:X4}"; - if (token.HasValues && token[tag].HasValues) + if (token is not null && token.ContainsKey(tag)) { - return token[tag]?["Value"]?.First.ToString(); + return token[tag].AsValue().GetValue(); } return defaultValue; diff --git a/src/DicomWebClient/CLI/Wado.cs b/src/DicomWebClient/CLI/Wado.cs index c2e343d99..fa209dde2 100644 --- a/src/DicomWebClient/CLI/Wado.cs +++ b/src/DicomWebClient/CLI/Wado.cs @@ -176,8 +176,8 @@ public async Task Bulk( private async Task SaveJson(string outputDir, IAsyncEnumerable enumerable) { - Guard.Against.NullOrWhiteSpace(outputDir); - Guard.Against.Null(enumerable); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); + Guard.Against.Null(enumerable, nameof(enumerable)); await foreach (var item in enumerable) { @@ -187,8 +187,8 @@ private async Task SaveJson(string outputDir, IAsyncEnumerable enumerabl private async Task SaveFiles(string outputDir, IAsyncEnumerable enumerable) { - Guard.Against.NullOrWhiteSpace(outputDir); - Guard.Against.Null(enumerable); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); + Guard.Against.Null(enumerable, nameof(enumerable)); var count = 0; await foreach (var file in enumerable) @@ -201,7 +201,7 @@ private async Task SaveFiles(string outputDir, IAsyncEnumerable enume private void ValidateOutputFilename(ref string filename) { - Guard.Against.NullOrWhiteSpace(filename); + Guard.Against.NullOrWhiteSpace(filename, nameof(filename)); try { @@ -216,7 +216,7 @@ private void ValidateOutputFilename(ref string filename) private void ValidateOutputDirectory(ref string outputDir) { - Guard.Against.NullOrWhiteSpace(outputDir); + Guard.Against.NullOrWhiteSpace(outputDir, nameof(outputDir)); if (outputDir == ".") { @@ -230,8 +230,8 @@ private void ValidateOutputDirectory(ref string outputDir) private void ValidateOptions(string rootUrl, string transferSyntaxes, out Uri rootUri, out List dicomTransferSyntaxes) { - Guard.Against.NullOrWhiteSpace(rootUrl); - Guard.Against.NullOrWhiteSpace(transferSyntaxes); + Guard.Against.NullOrWhiteSpace(rootUrl, nameof(rootUrl)); + Guard.Against.NullOrWhiteSpace(transferSyntaxes, nameof(transferSyntaxes)); _logger.LogInformation("Checking arguments..."); rootUri = new Uri(rootUrl); diff --git a/src/DicomWebClient/CLI/packages.lock.json b/src/DicomWebClient/CLI/packages.lock.json index a7d23637f..102c5959a 100644 --- a/src/DicomWebClient/CLI/packages.lock.json +++ b/src/DicomWebClient/CLI/packages.lock.json @@ -1,7 +1,7 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "ConsoleAppFramework": { "type": "Direct", "requested": "[4.2.4, )", @@ -12,84 +12,49 @@ "System.Text.Json": "6.0.1" } }, - "fo-dicom": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", - "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Channels": "6.0.0" - } - }, - "Microsoft.Extensions.Http": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, - "JetBrains.Annotations": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, - "Microsoft.AspNet.WebApi.Client": { + "fo-dicom": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.CSharp": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "P+MBhIM0YX+JqROuf7i306ZLJEjQYA9uUyRDE+OqwUI5sh41e2ZbPQV3LfAPh+29cmceE1pUffXsGfR4eMY3KA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.Extensions.Configuration": { "type": "Transitive", @@ -171,8 +136,8 @@ }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -354,369 +319,11 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "Microsoft.Net.Http.Headers": { - "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ebWzW9j2nwxQeBo59As2TYn7nYr9BHicqqCwHOD1Vdo+50HBtLPuqdiCYJcLdTRknpYis/DSEOQz5KmZxwrIAg==", - "dependencies": { - "Microsoft.CSharp": "4.3.0", - "System.Collections": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Runtime.Serialization.Formatters": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - } - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Collections.NonGeneric": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Collections.Specialized": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.ComponentModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", - "dependencies": { - "System.ComponentModel": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.TypeConverter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.NonGeneric": "4.3.0", - "System.Collections.Specialized": "4.3.0", - "System.ComponentModel": "4.3.0", - "System.ComponentModel.Primitives": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -730,764 +337,42 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Dynamic.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Async": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Runtime.Serialization.Formatters": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0" - } - }, - "System.Runtime.Serialization.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==", - "dependencies": { - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, - "System.Xml.XmlDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" } } } diff --git a/src/DicomWebClient/Common/HttpResponseMessageExtension.cs b/src/DicomWebClient/Common/HttpResponseMessageExtension.cs index bd95e9d01..a4e729527 100644 --- a/src/DicomWebClient/Common/HttpResponseMessageExtension.cs +++ b/src/DicomWebClient/Common/HttpResponseMessageExtension.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2020 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,7 +31,7 @@ internal static class HttpMessageExtension { public static void AddRange(this HttpRequestMessage request, Tuple byteRange = null) { - Guard.Against.Null(request); + Guard.Against.Null(request, nameof(request)); if (byteRange is null) { request.Headers.Add(HeaderNames.Range, "byte=0-"); @@ -45,25 +45,25 @@ public static void AddRange(this HttpRequestMessage request, Tuple by public static async IAsyncEnumerable ToDicomAsyncEnumerable(this HttpResponseMessage response) { - Guard.Against.Null(response); + Guard.Against.Null(response, nameof(response)); Guard.Against.Null(response.Content, nameof(response.Content)); await foreach (var buffer in DecodeMultipartMessage(response)) { using (var memoryStream = new MemoryStream(buffer)) { - yield return await DicomFile.OpenAsync(memoryStream, FileReadOption.ReadAll); + yield return await DicomFile.OpenAsync(memoryStream, FileReadOption.ReadAll).ConfigureAwait(false); } } } public static async Task ToBinaryData(this HttpResponseMessage response) { - Guard.Against.Null(response); + Guard.Against.Null(response, nameof(response)); using (var memoryStream = new MemoryStream()) { await foreach (var buffer in DecodeMultipartMessage(response)) { - await memoryStream.WriteAsync(buffer, 0, buffer.Length); + await memoryStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); } return memoryStream.ToArray(); } @@ -71,7 +71,7 @@ public static async Task ToBinaryData(this HttpResponseMessage response) private static async IAsyncEnumerable DecodeMultipartMessage(HttpResponseMessage response) { - Guard.Against.Null(response); + Guard.Against.Null(response, nameof(response)); var contentType = response.Content.Headers.ContentType; if (contentType.MediaType != MimeMappings.MultiPartRelated) { diff --git a/src/DicomWebClient/DicomWebClient.cs b/src/DicomWebClient/DicomWebClient.cs index 6693e77db..d86ccfd22 100644 --- a/src/DicomWebClient/DicomWebClient.cs +++ b/src/DicomWebClient/DicomWebClient.cs @@ -49,7 +49,7 @@ public class DicomWebClient : IDicomWebClient /// Optional logger for capturing client logs. public DicomWebClient(HttpClient httpClient, ILogger logger) { - Guard.Against.Null(httpClient); + Guard.Against.Null(httpClient, nameof(httpClient)); _httpClient = httpClient; _logger = logger; @@ -79,10 +79,11 @@ public void ConfigureServiceUris(Uri uriRoot) /// #pragma warning disable CA1054 + public void ConfigureServicePrefix(DicomWebServiceType serviceType, string urlPrefix) #pragma warning restore CA1054 { - Guard.Against.NullOrWhiteSpace(urlPrefix); + Guard.Against.NullOrWhiteSpace(urlPrefix, nameof(urlPrefix)); switch (serviceType) { @@ -112,7 +113,7 @@ public void ConfigureServicePrefix(DicomWebServiceType serviceType, string urlPr /// public void ConfigureAuthentication(AuthenticationHeaderValue value) { - Guard.Against.Null(value); + Guard.Against.Null(value, nameof(value)); _httpClient.DefaultRequestHeaders.Authorization = value; } diff --git a/src/DicomWebClient/AssemblyInfo.cs b/src/DicomWebClient/InternalVisibleTo.cs similarity index 100% rename from src/DicomWebClient/AssemblyInfo.cs rename to src/DicomWebClient/InternalVisibleTo.cs diff --git a/src/DicomWebClient/Monai.Deploy.InformaticsGateway.DicomWeb.Client.csproj b/src/DicomWebClient/Monai.Deploy.InformaticsGateway.DicomWeb.Client.csproj index c2af0ca26..84520237e 100644 --- a/src/DicomWebClient/Monai.Deploy.InformaticsGateway.DicomWeb.Client.csproj +++ b/src/DicomWebClient/Monai.Deploy.InformaticsGateway.DicomWeb.Client.csproj @@ -13,49 +13,39 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - + + + - net6.0 + net8.0 9.0 Apache-2.0 true ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + false - - - - - - - - All - - - - - + + + + - - - + \ No newline at end of file diff --git a/src/DicomWebClient/Services/QidoService.cs b/src/DicomWebClient/Services/QidoService.cs index 8d2e3dc98..73d3c248c 100644 --- a/src/DicomWebClient/Services/QidoService.cs +++ b/src/DicomWebClient/Services/QidoService.cs @@ -71,7 +71,7 @@ public async IAsyncEnumerable SearchForStudies(IReadOnlyDictionary queries, bool fuzzyMatching, int limit, int offset) { - Guard.Against.Null(queries); + Guard.Against.Null(queries, nameof(queries)); if (fuzzyMatching) { queries.Add("fuzzymatching=true"); @@ -90,7 +90,7 @@ private void AppendQueryOptions(List queries, bool fuzzyMatching, int li private void AppendAdditionalFields(List queries, IReadOnlyList fieldsToInclude) { - Guard.Against.Null(queries); + Guard.Against.Null(queries, nameof(queries)); if (fieldsToInclude is null || fieldsToInclude.Count == 0) { @@ -105,7 +105,7 @@ private void AppendAdditionalFields(List queries, IReadOnlyList private void AppendQueryParameters(List queries, IReadOnlyDictionary queryParameters) { - Guard.Against.Null(queries); + Guard.Against.Null(queries, nameof(queries)); if (queryParameters is null || queryParameters.Count == 0) { diff --git a/src/DicomWebClient/Services/ServiceBase.cs b/src/DicomWebClient/Services/ServiceBase.cs index 6425c559e..0e5969f4d 100644 --- a/src/DicomWebClient/Services/ServiceBase.cs +++ b/src/DicomWebClient/Services/ServiceBase.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Text.Json.Nodes; using Ardalis.GuardClauses; using FellowOakDicom; using FellowOakDicom.Serialization; @@ -25,8 +26,6 @@ using Microsoft.Net.Http.Headers; using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.DicomWeb.Client.API; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Monai.Deploy.InformaticsGateway.DicomWeb.Client { @@ -44,7 +43,7 @@ protected ServiceBase(HttpClient httpClient, ILogger logger = null) public bool TryConfigureServiceUriPrefix(string uriPrefix) { - Guard.Against.NullOrWhiteSpace(uriPrefix); + Guard.Against.NullOrWhiteSpace(uriPrefix, nameof(uriPrefix)); if (HttpClient.BaseAddress is null) { @@ -79,12 +78,12 @@ protected async IAsyncEnumerable GetMetadata(Uri uri) response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var jsonArray = JArray.Parse(json); - foreach (var item in jsonArray.Children()) + var jsonArray = JsonNode.Parse(json); + foreach (var item in jsonArray.AsArray()) { if (typeof(T) == typeof(string)) { - yield return (T)(object)item.ToString(Formatting.Indented); + yield return (T)(object)item.ToString(); } else if (typeof(T) == typeof(DicomDataset)) { diff --git a/src/DicomWebClient/Services/StowService.cs b/src/DicomWebClient/Services/StowService.cs index c64986a75..44e44dbef 100644 --- a/src/DicomWebClient/Services/StowService.cs +++ b/src/DicomWebClient/Services/StowService.cs @@ -45,7 +45,7 @@ public StowService(HttpClient httpClient, ILogger logger = null) /// public async Task> Store(string studyInstanceUid, IEnumerable dicomFiles, CancellationToken cancellationToken = default) { - Guard.Against.NullOrEmpty(dicomFiles); + Guard.Against.NullOrEmpty(dicomFiles, nameof(dicomFiles)); var postUri = GetStudiesUri(studyInstanceUid); diff --git a/src/DicomWebClient/Services/WadoService.cs b/src/DicomWebClient/Services/WadoService.cs index 67250e67a..029dc0bcc 100644 --- a/src/DicomWebClient/Services/WadoService.cs +++ b/src/DicomWebClient/Services/WadoService.cs @@ -17,7 +17,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Ardalis.GuardClauses; @@ -41,7 +40,7 @@ public async IAsyncEnumerable Retrieve( string studyInstanceUid, params DicomTransferSyntax[] transferSyntaxes) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); var studyUri = GetStudiesUri(studyInstanceUid); @@ -66,9 +65,9 @@ public async IAsyncEnumerable Retrieve( string seriesInstanceUid, params DicomTransferSyntax[] transferSyntaxes) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); DicomValidation.ValidateUI(seriesInstanceUid); var seriesUri = GetSeriesUri(studyInstanceUid, seriesInstanceUid); @@ -94,11 +93,11 @@ public async Task Retrieve( string sopInstanceUid, params DicomTransferSyntax[] transferSyntaxes) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); DicomValidation.ValidateUI(seriesInstanceUid); - Guard.Against.NullOrWhiteSpace(sopInstanceUid); + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); DicomValidation.ValidateUI(sopInstanceUid); var instanceUri = GetInstanceUri(studyInstanceUid, seriesInstanceUid, sopInstanceUid); @@ -114,7 +113,10 @@ public async Task Retrieve( try { - return await response.ToDicomAsyncEnumerable().FirstOrDefaultAsync(); + await using (var enumerator = response.ToDicomAsyncEnumerable().GetAsyncEnumerator()) + { + return await enumerator.MoveNextAsync() ? enumerator.Current : null; + } } catch (Exception ex) { @@ -153,11 +155,11 @@ public async Task Retrieve( Tuple byteRange = null, params DicomTransferSyntax[] transferSyntaxes) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); DicomValidation.ValidateUI(seriesInstanceUid); - Guard.Against.NullOrWhiteSpace(sopInstanceUid); + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); DicomValidation.ValidateUI(sopInstanceUid); return await Retrieve(new Uri($"{RequestServicePrefix}studies/{studyInstanceUid}/series/{seriesInstanceUid}/instances/{sopInstanceUid}/bulk/{dicomTag.Group:X4}{dicomTag.Element:X4}", UriKind.Relative), byteRange, transferSyntaxes); @@ -175,7 +177,7 @@ public async Task Retrieve( Tuple byteRange = null, params DicomTransferSyntax[] transferSyntaxes) { - Guard.Against.Null(bulkdataUri); + Guard.Against.Null(bulkdataUri, nameof(bulkdataUri)); if (bulkdataUri.IsAbsoluteUri) { @@ -200,7 +202,7 @@ public async Task Retrieve( public async IAsyncEnumerable RetrieveMetadata( string studyInstanceUid) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); var studyUri = GetStudiesUri(studyInstanceUid); var studyMetadataUri = new Uri($"{studyUri}metadata", UriKind.Relative); @@ -217,9 +219,9 @@ public async IAsyncEnumerable RetrieveMetadata( string studyInstanceUid, string seriesInstanceUid) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); DicomValidation.ValidateUI(seriesInstanceUid); var seriesUri = GetSeriesUri(studyInstanceUid, seriesInstanceUid); @@ -237,11 +239,11 @@ public async Task RetrieveMetadata( string seriesInstanceUid, string sopInstanceUid) { - Guard.Against.NullOrWhiteSpace(studyInstanceUid); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); DicomValidation.ValidateUI(studyInstanceUid); - Guard.Against.NullOrWhiteSpace(seriesInstanceUid); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); DicomValidation.ValidateUI(seriesInstanceUid); - Guard.Against.NullOrWhiteSpace(sopInstanceUid); + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); DicomValidation.ValidateUI(sopInstanceUid); var instanceUri = GetInstanceUri(studyInstanceUid, seriesInstanceUid, sopInstanceUid); @@ -250,7 +252,10 @@ public async Task RetrieveMetadata( try { - return await GetMetadata(instancMetadataUri).FirstOrDefaultAsync(); + await using (var enumerator = GetMetadata(instancMetadataUri).GetAsyncEnumerator()) + { + return await enumerator.MoveNextAsync() ? enumerator.Current : default; + } } catch (Exception ex) when (ex is not UnsupportedReturnTypeException) { diff --git a/src/DicomWebClient/Test/DicomWebClientExceptionTest.cs b/src/DicomWebClient/Test/DicomWebClientExceptionTest.cs deleted file mode 100644 index 05d57a152..000000000 --- a/src/DicomWebClient/Test/DicomWebClientExceptionTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2022 MONAI Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using Monai.Deploy.InformaticsGateway.DicomWeb.Client.API; -using Xunit; - -namespace Monai.Deploy.InformaticsGateway.DicomWebClient.Test -{ - public class DicomWebClientExceptionTest - { - [Fact] - public void TestControlExceptionSerialization() - { - var exception = new DicomWebClientException(System.Net.HttpStatusCode.OK, "message", new System.Exception("bla")); - - var data = SerializeToBytes(exception); - var result = DeserializeFromBytes(data); - - Assert.Equal(exception.Message, result.Message); - } - - private static byte[] SerializeToBytes(T e) - { - using var stream = new MemoryStream(); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - new BinaryFormatter().Serialize(stream, e); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - return stream.GetBuffer(); - } - - private static T DeserializeFromBytes(byte[] bytes) - { - using var stream = new MemoryStream(bytes); -#pragma warning disable SYSLIB0011 // Type or member is obsolete - return (T)new BinaryFormatter().Deserialize(stream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - } - } -} diff --git a/src/DicomWebClient/Test/HttpMessageExtensionTest.cs b/src/DicomWebClient/Test/HttpMessageExtensionTest.cs index f68a79232..b28509e31 100644 --- a/src/DicomWebClient/Test/HttpMessageExtensionTest.cs +++ b/src/DicomWebClient/Test/HttpMessageExtensionTest.cs @@ -172,7 +172,7 @@ public async Task ToBinaryData_Ok() AddByteArrayContent(multipartContent); message.Content = multipartContent; - var result = await message.ToBinaryData().ConfigureAwait(false); + var result = await message.ToBinaryData().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(_byteData, result); } diff --git a/src/DicomWebClient/Test/Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test.csproj b/src/DicomWebClient/Test/Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test.csproj index 8dc7fdd46..bf769a5ea 100644 --- a/src/DicomWebClient/Test/Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test.csproj +++ b/src/DicomWebClient/Test/Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test.csproj @@ -1,5 +1,5 @@ - - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test Apache-2.0 false true - - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - + \ No newline at end of file diff --git a/src/DicomWebClient/Test/packages.lock.json b/src/DicomWebClient/Test/packages.lock.json index 4734d7ead..4932189f7 100644 --- a/src/DicomWebClient/Test/packages.lock.json +++ b/src/DicomWebClient/Test/packages.lock.json @@ -1,119 +1,119 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "Ardalis.GuardClauses": { "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } + "requested": "[4.6.0, )", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "xRetry": { "type": "Direct", - "requested": "[1.8.0, )", - "resolved": "1.8.0", - "contentHash": "H8KXWHBjQASwD4y/7L2j7j4KLmg8z4+mCV4atrhZvJVnCkVSKLkWe1lfKGmaCYkKt2dJnC4yH+tJXGqthSkGGg==", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", "dependencies": { "xunit.core": "[2.4.0, 3.0.0)" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.Bcl.AsyncInterfaces": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.CodeCoverage": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -124,17 +124,6 @@ "resolved": "6.0.0", "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" }, - "Microsoft.Extensions.Http": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } - }, "Microsoft.Extensions.Logging": { "type": "Transitive", "resolved": "6.0.0", @@ -169,297 +158,33 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "Microsoft.Net.Http.Headers": { - "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" - } - }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, "Newtonsoft.Json": { "type": "Transitive", "resolved": "13.0.1", "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } - }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -473,693 +198,36 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Async": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, "System.Reflection.Metadata": { "type": "Transitive", "resolved": "1.6.0", "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, "xunit.abstractions": { "type": "Transitive", "resolved": "2.0.3", @@ -1167,61 +235,50 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" } } } diff --git a/src/DicomWebClient/Third-party/Microsoft/Extensions.cs b/src/DicomWebClient/Third-party/Microsoft/Extensions.cs new file mode 100644 index 000000000..2c269e449 --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/Extensions.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics.Contracts; +using System.Net.Http.Headers; + + +namespace System.Net.Http +{ + internal static class Extensions + { + public static void CopyTo(this HttpContentHeaders fromHeaders, HttpContentHeaders toHeaders) + { + Contract.Assert(fromHeaders != null, "fromHeaders cannot be null."); + Contract.Assert(toHeaders != null, "toHeaders cannot be null."); + + foreach (var header in fromHeaders) + { + toHeaders.TryAddWithoutValidation(header.Key, header.Value); + } + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/InternetMessageFormatHeaderParser.cs b/src/DicomWebClient/Third-party/Microsoft/InternetMessageFormatHeaderParser.cs new file mode 100644 index 000000000..e7738ec7e --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/InternetMessageFormatHeaderParser.cs @@ -0,0 +1,372 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Net.Http.Headers; +using System.Text; +using Ardalis.GuardClauses; + +namespace System.Net.Http.Formatting.Parsers +{ + /// + /// Represents the overall state of various parsers. + /// + internal enum ParserState + { + /// + /// Need more data + /// + NeedMoreData = 0, + + /// + /// Parsing completed (final) + /// + Done, + + /// + /// Bad data format (final) + /// + Invalid, + + /// + /// Data exceeds the allowed size (final) + /// + DataTooBig, + } + + /// + /// Buffer-oriented RFC 5322 style Internet Message Format parser which can be used to pass header + /// fields used in HTTP and MIME message entities. + /// + internal class InternetMessageFormatHeaderParser + { + internal const int MinHeaderSize = 2; + + private int _totalBytesConsumed; + private readonly int _maxHeaderSize; + + private HeaderFieldState _headerState; + private readonly HttpHeaders _headers; + private readonly CurrentHeaderFieldStore _currentHeader; + private readonly bool _ignoreHeaderValidation; + + /// + /// Initializes a new instance of the class. + /// + /// Concrete instance where header fields are added as they are parsed. + /// Maximum length of complete header containing all the individual header fields. + public InternetMessageFormatHeaderParser(HttpHeaders headers, int maxHeaderSize) + : this(headers, maxHeaderSize, ignoreHeaderValidation: false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Concrete instance where header fields are added as they are parsed. + /// + /// + /// Maximum length of complete header containing all the individual header fields. + /// + /// + /// Will validate content and names of headers if set to false. + /// + public InternetMessageFormatHeaderParser(HttpHeaders headers, int maxHeaderSize, bool ignoreHeaderValidation) + { + // The minimum length which would be an empty header terminated by CRLF + if (maxHeaderSize < InternetMessageFormatHeaderParser.MinHeaderSize) + { + throw new ArgumentException("maxHeaderSize"); + } + + Guard.Against.Null(headers, nameof(headers)); + + _headers = headers; + _maxHeaderSize = maxHeaderSize; + _ignoreHeaderValidation = ignoreHeaderValidation; + _currentHeader = new CurrentHeaderFieldStore(); + } + + private enum HeaderFieldState + { + Name = 0, + Value, + AfterCarriageReturn, + FoldingLine + } + + /// + /// Parse a buffer of RFC 5322 style header fields and add them to the collection. + /// Bytes are parsed in a consuming manner from the beginning of the buffer meaning that the same bytes can not be + /// present in the buffer. + /// + /// Request buffer from where request is read + /// Size of request buffer + /// Offset into request buffer + /// State of the parser. Call this method with new data until it reaches a final state. + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] + public ParserState ParseBuffer( + byte[] buffer, + int bytesReady, + ref int bytesConsumed) + { + Guard.Against.Null(buffer, nameof(buffer)); + + ParserState parseStatus = ParserState.NeedMoreData; + + if (bytesConsumed >= bytesReady) + { + // We already can tell we need more data + return parseStatus; + } + + try + { + parseStatus = InternetMessageFormatHeaderParser.ParseHeaderFields( + buffer, + bytesReady, + ref bytesConsumed, + ref _headerState, + _maxHeaderSize, + ref _totalBytesConsumed, + _currentHeader, + _headers, + _ignoreHeaderValidation); + } + catch (Exception) + { + parseStatus = ParserState.Invalid; + } + + return parseStatus; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] + private static ParserState ParseHeaderFields( + byte[] buffer, + int bytesReady, + ref int bytesConsumed, + ref HeaderFieldState requestHeaderState, + int maximumHeaderLength, + ref int totalBytesConsumed, + CurrentHeaderFieldStore currentField, + HttpHeaders headers, + bool ignoreHeaderValidation) + { + Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseHeaderFields()|(inputBufferLength - bytesParsed) < 0"); + Contract.Assert(maximumHeaderLength <= 0 || totalBytesConsumed <= maximumHeaderLength, "ParseHeaderFields()|Headers already read exceeds limit."); + + // Remember where we started. + int initialBytesParsed = bytesConsumed; + int segmentStart; + + // Set up parsing status with what will happen if we exceed the buffer. + ParserState parseStatus = ParserState.DataTooBig; + int effectiveMax = maximumHeaderLength <= 0 ? Int32.MaxValue : maximumHeaderLength - totalBytesConsumed + initialBytesParsed; + if (bytesReady < effectiveMax) + { + parseStatus = ParserState.NeedMoreData; + effectiveMax = bytesReady; + } + + Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); + + switch (requestHeaderState) + { + case HeaderFieldState.Name: + segmentStart = bytesConsumed; + while (buffer[bytesConsumed] != ':') + { + if (buffer[bytesConsumed] == '\r') + { + if (!currentField.IsEmpty()) + { + parseStatus = ParserState.Invalid; + goto quit; + } + else + { + // Move past the '\r' + requestHeaderState = HeaderFieldState.AfterCarriageReturn; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.AfterCarriageReturn; + } + } + + if (++bytesConsumed == effectiveMax) + { + string headerFieldName = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); + currentField.Name.Append(headerFieldName); + goto quit; + } + } + + if (bytesConsumed > segmentStart) + { + string headerFieldName = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); + currentField.Name.Append(headerFieldName); + } + + // Move past the ':' + requestHeaderState = HeaderFieldState.Value; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.Value; + + case HeaderFieldState.Value: + segmentStart = bytesConsumed; + while (buffer[bytesConsumed] != '\r') + { + if (++bytesConsumed == effectiveMax) + { + string headerFieldValue = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); + currentField.Value.Append(headerFieldValue); + goto quit; + } + } + + if (bytesConsumed > segmentStart) + { + string headerFieldValue = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart); + currentField.Value.Append(headerFieldValue); + } + + // Move past the CR + requestHeaderState = HeaderFieldState.AfterCarriageReturn; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.AfterCarriageReturn; + + case HeaderFieldState.AfterCarriageReturn: + if (buffer[bytesConsumed] != '\n') + { + parseStatus = ParserState.Invalid; + goto quit; + } + + if (currentField.IsEmpty()) + { + parseStatus = ParserState.Done; + bytesConsumed++; + goto quit; + } + + requestHeaderState = HeaderFieldState.FoldingLine; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.FoldingLine; + + case HeaderFieldState.FoldingLine: + if (buffer[bytesConsumed] != ' ' && buffer[bytesConsumed] != '\t') + { + currentField.CopyTo(headers, ignoreHeaderValidation); + requestHeaderState = HeaderFieldState.Name; + if (bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.Name; + } + + // Unfold line by inserting SP instead + currentField.Value.Append(' '); + + // Continue parsing header field value + requestHeaderState = HeaderFieldState.Value; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case HeaderFieldState.Value; + } + + quit: + totalBytesConsumed += bytesConsumed - initialBytesParsed; + return parseStatus; + } + + /// + /// Maintains information about the current header field being parsed. + /// + private class CurrentHeaderFieldStore + { + private const int DefaultFieldNameAllocation = 128; + private const int DefaultFieldValueAllocation = 2 * 1024; + + private static readonly char[] LinearWhiteSpace = new char[] { ' ', '\t' }; + + /// + /// Gets the header field name. + /// + public StringBuilder Name { get; } = new StringBuilder(CurrentHeaderFieldStore.DefaultFieldNameAllocation); + + /// + /// Gets the header field value. + /// + public StringBuilder Value { get; } = new StringBuilder(CurrentHeaderFieldStore.DefaultFieldValueAllocation); + + /// + /// Copies current header field to the provided instance. + /// + /// The headers. + /// Set to false to validate headers + public void CopyTo(HttpHeaders headers, bool ignoreHeaderValidation) + { + var name = Name.ToString(); + var value = Value.ToString().Trim(CurrentHeaderFieldStore.LinearWhiteSpace); + if (string.Equals("expires", name, StringComparison.OrdinalIgnoreCase)) + { + ignoreHeaderValidation = true; + } + + if (ignoreHeaderValidation) + { + headers.TryAddWithoutValidation(name, value); + } + else + { + headers.Add(name, value); + } + + Clear(); + } + + /// + /// Determines whether this instance is empty. + /// + /// + /// true if this instance is empty; otherwise, false. + /// + public bool IsEmpty() + { + return Name.Length == 0 && Value.Length == 0; + } + + /// + /// Clears this instance. + /// + private void Clear() + { + Name.Clear(); + Value.Clear(); + } + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MimeBodyPart.cs b/src/DicomWebClient/Third-party/Microsoft/MimeBodyPart.cs new file mode 100644 index 000000000..ebdace09b --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MimeBodyPart.cs @@ -0,0 +1,230 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.IO; +using System.Net.Http.Formatting.Parsers; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + /// + /// Maintains information about MIME body parts parsed by . + /// + internal class MimeBodyPart : IDisposable + { + private static readonly Type StreamType = typeof(Stream); + private Stream _outputStream; + private readonly MultipartStreamProvider _streamProvider; + private HttpContent _parentContent; + private HttpContent _content; + private readonly HttpContentHeaders _headers; + + /// + /// Initializes a new instance of the class. + /// + /// The stream provider. + /// The max length of the MIME header within each MIME body part. + /// The part's parent content + public MimeBodyPart(MultipartStreamProvider streamProvider, int maxBodyPartHeaderSize, HttpContent parentContent) + { + Contract.Assert(streamProvider != null); + Contract.Assert(parentContent != null); + _streamProvider = streamProvider; + _parentContent = parentContent; + Segments = new List>(2); + _headers = CreateEmptyContentHeaders(); + HeaderParser = new InternetMessageFormatHeaderParser( + _headers, + maxBodyPartHeaderSize, + ignoreHeaderValidation: true); + } + + /// + /// Gets the header parser. + /// + /// + /// The header parser. + /// + public InternetMessageFormatHeaderParser HeaderParser { get; private set; } + + /// + /// Gets the part's content as an HttpContent. + /// + /// + /// The part's content, or null if the part had no content. + /// + public HttpContent GetCompletedHttpContent() + { + Contract.Assert(IsComplete); + + if (_content == null) + { + return null; + } + + _headers.CopyTo(_content.Headers); + return _content; + } + + /// + /// Gets the set of pointing to the read buffer with + /// contents of this body part. + /// + public List> Segments { get; private set; } + + /// + /// Gets or sets a value indicating whether the body part has been completed. + /// + /// + /// true if this instance is complete; otherwise, false. + /// + public bool IsComplete { get; set; } + + /// + /// Gets or sets a value indicating whether this is the final body part. + /// + /// + /// true if this instance is complete; otherwise, false. + /// + public bool IsFinal { get; set; } + + /// + /// Writes the into the part's output stream. + /// + /// The current segment to be written to the part's output stream. + /// The token to monitor for cancellation requests. + public async Task WriteSegment(ArraySegment segment, CancellationToken cancellationToken) + { + var stream = GetOutputStream(); + await stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken); + } + + /// + /// Gets the output stream. + /// + /// The output stream to write the body part to. + private Stream GetOutputStream() + { + if (_outputStream == null) + { + try + { + _outputStream = _streamProvider.GetStream(_parentContent, _headers); + } + catch (Exception e) + { + throw new InvalidOperationException($"The stream provider of type '{_streamProvider.GetType().Name}' threw an exception.", e); + } + + if (_outputStream == null) + { + throw new InvalidOperationException($"The stream provider of type '{_streamProvider.GetType().Name}' returned null. It must return a writable '{StreamType.Name}' instance."); + } + + if (!_outputStream.CanWrite) + { + throw new InvalidOperationException($"The stream provider of type '{_streamProvider.GetType().Name}' returned a read-only stream. It must return a writable '{StreamType.Name}' instance."); + } + _content = new StreamContent(_outputStream); + } + + return _outputStream; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected void Dispose(bool disposing) + { + if (disposing) + { + CleanupOutputStream(); + CleanupHttpContent(); + _parentContent = null; + HeaderParser = null; + Segments.Clear(); + } + } + + /// + /// In the success case, the HttpContent is to be used after this Part has been parsed and disposed of. + /// Only if Dispose has been called on a non-completed part, the parsed HttpContent needs to be disposed of as well. + /// + private void CleanupHttpContent() + { + if (!IsComplete && _content != null) + { + _content.Dispose(); + } + + _content = null; + } + + /// + /// Resets the output stream by either closing it or, in the case of a resetting + /// position to 0 so that it can be read by the caller. + /// + private void CleanupOutputStream() + { + if (_outputStream != null) + { + MemoryStream output = _outputStream as MemoryStream; + if (output != null) + { + output.Position = 0; + } + else + { +#if NETSTANDARD1_3 + _outputStream.Dispose(); +#else + _outputStream.Close(); +#endif + } + + _outputStream = null; + } + } + + /// + /// Creates an empty instance. The only way is to get it from a dummy + /// instance. + /// + /// The created instance. + public static HttpContentHeaders CreateEmptyContentHeaders() + { + HttpContent tempContent = null; + HttpContentHeaders contentHeaders = null; + try + { + tempContent = new StringContent(String.Empty); + contentHeaders = tempContent.Headers; + contentHeaders.Clear(); + } + finally + { + // We can dispose the content without touching the headers + if (tempContent != null) + { + tempContent.Dispose(); + } + } + + return contentHeaders; + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MimeMultipartBodyPartParser.cs b/src/DicomWebClient/Third-party/Microsoft/MimeMultipartBodyPartParser.cs new file mode 100644 index 000000000..a9a4428e7 --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MimeMultipartBodyPartParser.cs @@ -0,0 +1,310 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.IO; +using System.Net.Http.Headers; + +namespace System.Net.Http.Formatting.Parsers +{ + /// + /// Complete MIME multipart parser that combines for parsing the MIME message into individual body parts + /// and for parsing each body part into a MIME header and a MIME body. The caller of the parser is returned + /// the resulting MIME bodies which can then be written to some output. + /// + internal class MimeMultipartBodyPartParser : IDisposable + { + internal const long DefaultMaxMessageSize = Int64.MaxValue; + private const int DefaultMaxBodyPartHeaderSize = 4 * 1024; + + // MIME parser + private MimeMultipartParser _mimeParser; + private MimeMultipartParser.State _mimeStatus = MimeMultipartParser.State.NeedMoreData; + private readonly ArraySegment[] _parsedBodyPart = new ArraySegment[2]; + private MimeBodyPart _currentBodyPart; + private bool _isFirst = true; + + // Header field parser + private ParserState _bodyPartHeaderStatus = ParserState.NeedMoreData; + private readonly int _maxBodyPartHeaderSize; + + // Stream provider + private readonly MultipartStreamProvider _streamProvider; + + private readonly HttpContent _content; + + /// + /// Initializes a new instance of the class. + /// + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + public MimeMultipartBodyPartParser(HttpContent content, MultipartStreamProvider streamProvider) + : this(content, streamProvider, DefaultMaxMessageSize, DefaultMaxBodyPartHeaderSize) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + /// The max length of the entire MIME multipart message. + /// The max length of the MIME header within each MIME body part. + public MimeMultipartBodyPartParser( + HttpContent content, + MultipartStreamProvider streamProvider, + long maxMessageSize, + int maxBodyPartHeaderSize) + { + Contract.Assert(content != null, "content cannot be null."); + Contract.Assert(streamProvider != null, "streamProvider cannot be null."); + + string boundary = ValidateArguments(content, maxMessageSize, true); + + _mimeParser = new MimeMultipartParser(boundary, maxMessageSize); + _currentBodyPart = new MimeBodyPart(streamProvider, maxBodyPartHeaderSize, content); + _content = content; + _maxBodyPartHeaderSize = maxBodyPartHeaderSize; + + _streamProvider = streamProvider; + } + + /// + /// Determines whether the specified content is MIME multipart content. + /// + /// The content. + /// + /// true if the specified content is MIME multipart content; otherwise, false. + /// + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to false return.")] + public static bool IsMimeMultipartContent(HttpContent content) + { + Contract.Assert(content != null, "content cannot be null."); + try + { + string boundary = ValidateArguments(content, DefaultMaxMessageSize, false); + return boundary != null; + } + catch (Exception) + { + return false; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Parses the data provided and generates parsed MIME body part bodies in the form of which are ready to + /// write to the output stream. + /// + /// The data to parse + /// The number of bytes available in the input data + /// Parsed instances. + public IEnumerable ParseBuffer(byte[] data, int bytesRead) + { + int bytesConsumed = 0; + bool isFinal = false; + + // There's a special case here - if we've reached the end of the message and there's no optional + // CRLF, then we're out of bytes to read, but we have finished the message. + // + // If IsWaitingForEndOfMessage is true and we're at the end of the stream, then we're going to + // call into the parser again with an empty array as the buffer to signal the end of the parse. + // Then the final boundary segment will be marked as complete. + if (bytesRead == 0 && !_mimeParser.IsWaitingForEndOfMessage) + { + CleanupCurrentBodyPart(); + throw new IOException("Unexpected end of MIME multipart stream. MIME multipart message is not complete."); + } + + // Make sure we remove an old array segments. + _currentBodyPart.Segments.Clear(); + + while (_mimeParser.CanParseMore(bytesRead, bytesConsumed)) + { + _mimeStatus = _mimeParser.ParseBuffer(data, bytesRead, ref bytesConsumed, out _parsedBodyPart[0], out _parsedBodyPart[1], out isFinal); + if (_mimeStatus != MimeMultipartParser.State.BodyPartCompleted && _mimeStatus != MimeMultipartParser.State.NeedMoreData) + { + CleanupCurrentBodyPart(); + throw new InvalidOperationException($"Error parsing MIME multipart message byte {bytesConsumed} of data segment {data}."); + } + + // First body is empty preamble which we just ignore + if (_isFirst) + { + if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted) + { + _isFirst = false; + } + + continue; + } + + // Parse the two array segments containing parsed body parts that the MIME parser gave us + foreach (ArraySegment part in _parsedBodyPart) + { + if (part.Count == 0) + { + continue; + } + + if (_bodyPartHeaderStatus != ParserState.Done) + { + int headerConsumed = part.Offset; + _bodyPartHeaderStatus = _currentBodyPart.HeaderParser.ParseBuffer(part.Array, part.Count + part.Offset, ref headerConsumed); + if (_bodyPartHeaderStatus == ParserState.Done) + { + // Add the remainder as body part content + _currentBodyPart.Segments.Add(new ArraySegment(part.Array, headerConsumed, part.Count + part.Offset - headerConsumed)); + } + else if (_bodyPartHeaderStatus != ParserState.NeedMoreData) + { + CleanupCurrentBodyPart(); + throw new InvalidOperationException($"Error parsing MIME multipart body part header byte {headerConsumed} of data segment {part.Array}."); + } + } + else + { + // Add the data as body part content + _currentBodyPart.Segments.Add(part); + } + } + + if (_mimeStatus == MimeMultipartParser.State.BodyPartCompleted) + { + // If body is completed then swap current body part + MimeBodyPart completed = _currentBodyPart; + completed.IsComplete = true; + completed.IsFinal = isFinal; + + _currentBodyPart = new MimeBodyPart(_streamProvider, _maxBodyPartHeaderSize, _content); + + _mimeStatus = MimeMultipartParser.State.NeedMoreData; + _bodyPartHeaderStatus = ParserState.NeedMoreData; + yield return completed; + } + else + { + // Otherwise return what we have + yield return _currentBodyPart; + } + } + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected void Dispose(bool disposing) + { + if (disposing) + { + _mimeParser = null; + CleanupCurrentBodyPart(); + } + } + + private static string ValidateArguments(HttpContent content, long maxMessageSize, bool throwOnError) + { + Contract.Assert(content != null, "content cannot be null."); + if (maxMessageSize < MimeMultipartParser.MinMessageSize) + { + if (throwOnError) + { + throw new ArgumentOutOfRangeException("maxMessageSize", $"Value must be greater than or equal to {MimeMultipartParser.MinMessageSize}."); + } + else + { + return null; + } + } + + MediaTypeHeaderValue contentType = content.Headers.ContentType; + if (contentType == null) + { + if (throwOnError) + { + throw new ArgumentException($"Invalid '{typeof(HttpContent).Name}' instance provided. It does not have a content-type header value. '{typeof(HttpContent).Name}' instances must have a content-type header starting with 'multipart/'.", "content"); + } + else + { + return null; + } + } + + if (!contentType.MediaType.StartsWith("multipart", StringComparison.OrdinalIgnoreCase)) + { + if (throwOnError) + { + throw new ArgumentException($"Invalid '{typeof(HttpContent).Name}' instance provided. It does not have a content type header starting with 'multipart/'.", "content"); + } + else + { + return null; + } + } + + string boundary = null; + foreach (NameValueHeaderValue p in contentType.Parameters) + { + if (p.Name.Equals("boundary", StringComparison.OrdinalIgnoreCase)) + { + boundary = UnquoteToken(p.Value); + break; + } + } + + if (boundary == null) + { + if (throwOnError) + { + throw new ArgumentException($"Invalid '{typeof(HttpContent).Name}' instance provided. It does not have a 'multipart' content-type header with a 'boundary' parameter.", "content"); + } + else + { + return null; + } + } + + return boundary; + } + + private void CleanupCurrentBodyPart() + { + if (_currentBodyPart != null) + { + _currentBodyPart.Dispose(); + _currentBodyPart = null; + } + } + + /// + /// Remove bounding quotes on a token if present + /// + /// Token to unquote. + /// Unquoted token. + public static string UnquoteToken(string token) + { + if (String.IsNullOrWhiteSpace(token)) + { + return token; + } + + if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1) + { + return token.Substring(1, token.Length - 2); + } + + return token; + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MimeMultipartParser.cs b/src/DicomWebClient/Third-party/Microsoft/MimeMultipartParser.cs new file mode 100644 index 000000000..67d438932 --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MimeMultipartParser.cs @@ -0,0 +1,790 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Text; +using Ardalis.GuardClauses; + +namespace System.Net.Http.Formatting.Parsers +{ + /// + /// Buffer-oriented MIME multipart parser. + /// + internal class MimeMultipartParser + { + internal const int MinMessageSize = 10; + + private const int MaxBoundarySize = 256; + + private const byte HTAB = 0x09; + private const byte SP = 0x20; + private const byte CR = 0x0D; + private const byte LF = 0x0A; + private const byte Dash = 0x2D; + private static readonly ArraySegment EmptyBodyPart = new ArraySegment(new byte[0]); + + private long _totalBytesConsumed; + private readonly long _maxMessageSize; + + private BodyPartState _bodyPartState; + private readonly string _boundary; + private readonly CurrentBodyPartStore _currentBoundary; + + /// + /// Initializes a new instance of the class. + /// + /// Message boundary + /// Maximum length of entire MIME multipart message. + public MimeMultipartParser(string boundary, long maxMessageSize) + { + // The minimum length which would be an empty message terminated by CRLF + if (maxMessageSize < MimeMultipartParser.MinMessageSize) + { + throw new ArgumentException("maxMessageSize"); + } + + Guard.Against.NullOrWhiteSpace(boundary, nameof(boundary)); + + if (boundary.Length > MaxBoundarySize - 10) + { + throw new ArgumentException("boundary"); + } + + if (boundary.EndsWith(" ", StringComparison.Ordinal)) + { + throw new ArgumentException("boundary"); + } + + _maxMessageSize = maxMessageSize; + _boundary = boundary; + _currentBoundary = new CurrentBodyPartStore(_boundary); + _bodyPartState = BodyPartState.AfterFirstLineFeed; + } + + public bool IsWaitingForEndOfMessage + { + get + { + return + _bodyPartState == BodyPartState.AfterBoundary && + _currentBoundary != null && + _currentBoundary.IsFinal; + } + } + + private enum BodyPartState + { + BodyPart = 0, + AfterFirstCarriageReturn, + AfterFirstLineFeed, + AfterFirstDash, + Boundary, + AfterBoundary, + AfterSecondDash, + AfterSecondCarriageReturn + } + + private enum MessageState + { + Boundary = 0, // about to parse boundary + BodyPart, // about to parse body-part + CloseDelimiter // about to read close-delimiter + } + + /// + /// Represents the overall state of the . + /// + public enum State + { + /// + /// Need more data + /// + NeedMoreData = 0, + + /// + /// Parsing of a complete body part succeeded. + /// + BodyPartCompleted, + + /// + /// Bad data format + /// + Invalid, + + /// + /// Data exceeds the allowed size + /// + DataTooBig, + } + + public bool CanParseMore(int bytesRead, int bytesConsumed) + { + if (bytesConsumed < bytesRead) + { + // If there's more bytes we haven't parsed, then we can parse more + return true; + } + + if (bytesRead == 0 && IsWaitingForEndOfMessage) + { + // If we're waiting for the end of the message and we've arrived there, we want parse to be called + // again so we can mark the parse as complete. + // + // This can happen when the last boundary segment doesn't have a trailing CRLF. We need to wait until + // the end of the message to complete the parse because we need to consume any trailing whitespace that's + //present. + return true; + } + + return false; + } + + /// + /// Parse a MIME multipart message. Bytes are parsed in a consuming + /// manner from the beginning of the request buffer meaning that the same bytes can not be + /// present in the request buffer. + /// + /// Request buffer from where request is read + /// Size of request buffer + /// Offset into request buffer + /// Any body part that was considered as a potential MIME multipart boundary but which was in fact part of the body. + /// The bulk of the body part. + /// Indicates whether the final body part has been found. + /// In order to get the complete body part, the caller is responsible for concatenating the contents of the + /// and out parameters. + /// State of the parser. + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is translated to parse state.")] + public State ParseBuffer( + byte[] buffer, + int bytesReady, + ref int bytesConsumed, + out ArraySegment remainingBodyPart, + out ArraySegment bodyPart, + out bool isFinalBodyPart) + { + Guard.Against.Null(buffer, nameof(buffer)); + + State parseStatus = State.NeedMoreData; + remainingBodyPart = MimeMultipartParser.EmptyBodyPart; + bodyPart = MimeMultipartParser.EmptyBodyPart; + isFinalBodyPart = false; + + try + { + parseStatus = MimeMultipartParser.ParseBodyPart( + buffer, + bytesReady, + ref bytesConsumed, + ref _bodyPartState, + _maxMessageSize, + ref _totalBytesConsumed, + _currentBoundary); + } + catch (Exception) + { + parseStatus = State.Invalid; + } + + remainingBodyPart = _currentBoundary.GetDiscardedBoundary(); + bodyPart = _currentBoundary.BodyPart; + if (parseStatus == State.BodyPartCompleted) + { + isFinalBodyPart = _currentBoundary.IsFinal; + _currentBoundary.ClearAll(); + } + else + { + _currentBoundary.ClearBodyPart(); + } + + return parseStatus; + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a parser which cannot be split up for performance reasons.")] + private static State ParseBodyPart( + byte[] buffer, + int bytesReady, + ref int bytesConsumed, + ref BodyPartState bodyPartState, + long maximumMessageLength, + ref long totalBytesConsumed, + CurrentBodyPartStore currentBodyPart) + { + Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseBodyPart()|(bytesReady - bytesConsumed) < 0"); + Contract.Assert(maximumMessageLength <= 0 || totalBytesConsumed <= maximumMessageLength, "ParseBodyPart()|Message already read exceeds limit."); + + // Remember where we started. + int segmentStart; + int initialBytesParsed = bytesConsumed; + + if (bytesReady == 0 && bodyPartState == BodyPartState.AfterBoundary && currentBodyPart.IsFinal) + { + // We've seen the end of the stream - the final body part has no trailing CRLF + return State.BodyPartCompleted; + } + + // Set up parsing status with what will happen if we exceed the buffer. + State parseStatus = State.DataTooBig; + long effectiveMax = maximumMessageLength <= 0 ? Int64.MaxValue : (maximumMessageLength - totalBytesConsumed + bytesConsumed); + if (effectiveMax == 0) + { + // effectiveMax is based on our max message size - if we've arrrived at the max size, then we need + // to stop parsing. + return State.DataTooBig; + } + + if (bytesReady <= effectiveMax) + { + parseStatus = State.NeedMoreData; + effectiveMax = bytesReady; + } + + currentBodyPart.ResetBoundaryOffset(); + + Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length."); + + switch (bodyPartState) + { + case BodyPartState.BodyPart: + while (buffer[bytesConsumed] != MimeMultipartParser.CR) + { + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + } + + // Remember potential boundary + currentBodyPart.AppendBoundary(MimeMultipartParser.CR); + + // Move past the CR + bodyPartState = BodyPartState.AfterFirstCarriageReturn; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.AfterFirstCarriageReturn; + + case BodyPartState.AfterFirstCarriageReturn: + if (buffer[bytesConsumed] != MimeMultipartParser.LF) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + // Remember potential boundary + currentBodyPart.AppendBoundary(MimeMultipartParser.LF); + + // Move past the CR + bodyPartState = BodyPartState.AfterFirstLineFeed; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.AfterFirstLineFeed; + + case BodyPartState.AfterFirstLineFeed: + if (buffer[bytesConsumed] == MimeMultipartParser.CR) + { + // Remember potential boundary + currentBodyPart.ResetBoundary(); + currentBodyPart.AppendBoundary(MimeMultipartParser.CR); + + // Move past the CR + bodyPartState = BodyPartState.AfterFirstCarriageReturn; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.AfterFirstCarriageReturn; + } + + if (buffer[bytesConsumed] != MimeMultipartParser.Dash) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + // Remember potential boundary + currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); + + // Move past the Dash + bodyPartState = BodyPartState.AfterFirstDash; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.AfterFirstDash; + + case BodyPartState.AfterFirstDash: + if (buffer[bytesConsumed] != MimeMultipartParser.Dash) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + // Remember potential boundary + currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); + + // Move past the Dash + bodyPartState = BodyPartState.Boundary; + if (++bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.Boundary; + + case BodyPartState.Boundary: + segmentStart = bytesConsumed; + while (buffer[bytesConsumed] != MimeMultipartParser.CR) + { + if (++bytesConsumed == effectiveMax) + { + if (currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) + { + if (currentBodyPart.IsBoundaryComplete()) + { + // At this point we've seen the end of a boundary segment that is aligned at the end + // of the buffer - this might be because we have another segment coming or it might + // truly be the end of the message. + bodyPartState = BodyPartState.AfterBoundary; + } + } + else + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + } + goto quit; + } + } + + if (bytesConsumed > segmentStart) + { + if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + } + + goto case BodyPartState.AfterBoundary; + + case BodyPartState.AfterBoundary: + + // This state means that we just saw the end of a boundary. It might by a 'normal' boundary, in which + // case it's followed by optional whitespace and a CRLF. Or it might be the 'final' boundary and will + // be followed by '--', optional whitespace and an optional CRLF. + if (buffer[bytesConsumed] == MimeMultipartParser.Dash && !currentBodyPart.IsFinal) + { + currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); + if (++bytesConsumed == effectiveMax) + { + bodyPartState = BodyPartState.AfterSecondDash; + goto quit; + } + + goto case BodyPartState.AfterSecondDash; + } + + // Capture optional whitespace + segmentStart = bytesConsumed; + while (buffer[bytesConsumed] != MimeMultipartParser.CR) + { + if (++bytesConsumed == effectiveMax) + { + if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) + { + // It's an unexpected character + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + } + + goto quit; + } + } + + if (bytesConsumed > segmentStart) + { + if (!currentBodyPart.AppendBoundary(buffer, segmentStart, bytesConsumed - segmentStart)) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + } + + if (buffer[bytesConsumed] == MimeMultipartParser.CR) + { + currentBodyPart.AppendBoundary(MimeMultipartParser.CR); + if (++bytesConsumed == effectiveMax) + { + bodyPartState = BodyPartState.AfterSecondCarriageReturn; + goto quit; + } + + goto case BodyPartState.AfterSecondCarriageReturn; + } + else + { + // It's an unexpected character + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + case BodyPartState.AfterSecondDash: + if (buffer[bytesConsumed] == MimeMultipartParser.Dash) + { + currentBodyPart.AppendBoundary(MimeMultipartParser.Dash); + bytesConsumed++; + + if (currentBodyPart.IsBoundaryComplete()) + { + Debug.Assert(currentBodyPart.IsFinal); + + // If we get in here, it means we've see the trailing '--' of the last boundary - in order to consume all of the + // remaining bytes, we don't mark the parse as complete again - wait until this method is called again with the + // empty buffer to do that. + bodyPartState = BodyPartState.AfterBoundary; + parseStatus = State.NeedMoreData; + goto quit; + } + else + { + currentBodyPart.ResetBoundary(); + if (bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.BodyPart; + } + } + else + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + case BodyPartState.AfterSecondCarriageReturn: + if (buffer[bytesConsumed] != MimeMultipartParser.LF) + { + currentBodyPart.ResetBoundary(); + bodyPartState = BodyPartState.BodyPart; + goto case BodyPartState.BodyPart; + } + + currentBodyPart.AppendBoundary(MimeMultipartParser.LF); + bytesConsumed++; + + bodyPartState = BodyPartState.BodyPart; + if (currentBodyPart.IsBoundaryComplete()) + { + parseStatus = State.BodyPartCompleted; + goto quit; + } + else + { + currentBodyPart.ResetBoundary(); + if (bytesConsumed == effectiveMax) + { + goto quit; + } + + goto case BodyPartState.BodyPart; + } + } + + quit: + if (initialBytesParsed < bytesConsumed) + { + int boundaryLength = currentBodyPart.BoundaryDelta; + if (boundaryLength > 0 && parseStatus != State.BodyPartCompleted) + { + currentBodyPart.HasPotentialBoundaryLeftOver = true; + } + + int bodyPartEnd = bytesConsumed - initialBytesParsed - boundaryLength; + + currentBodyPart.BodyPart = new ArraySegment(buffer, initialBytesParsed, bodyPartEnd); + } + + totalBytesConsumed += bytesConsumed - initialBytesParsed; + return parseStatus; + } + + /// + /// Maintains information about the current body part being parsed. + /// + [DebuggerDisplay("{DebuggerToString()}")] + private class CurrentBodyPartStore + { + private const int InitialOffset = 2; + + private readonly byte[] _boundaryStore = new byte[MaxBoundarySize]; + private int _boundaryStoreLength; + + private readonly byte[] _referenceBoundary = new byte[MaxBoundarySize]; + private readonly int _referenceBoundaryLength; + + private readonly byte[] _boundary = new byte[MaxBoundarySize]; + private int _boundaryLength = 0; + private bool _isFirst = true; + private bool _releaseDiscardedBoundary; + private int _boundaryOffset; + + /// + /// Initializes a new instance of the class. + /// + /// The reference boundary. + public CurrentBodyPartStore(string referenceBoundary) + { + Contract.Assert(referenceBoundary != null); + + _referenceBoundary[0] = MimeMultipartParser.CR; + _referenceBoundary[1] = MimeMultipartParser.LF; + _referenceBoundary[2] = MimeMultipartParser.Dash; + _referenceBoundary[3] = MimeMultipartParser.Dash; + _referenceBoundaryLength = 4 + Encoding.UTF8.GetBytes(referenceBoundary, 0, referenceBoundary.Length, _referenceBoundary, 4); + + _boundary[0] = MimeMultipartParser.CR; + _boundary[1] = MimeMultipartParser.LF; + _boundaryLength = CurrentBodyPartStore.InitialOffset; + } + + /// + /// Gets or sets a value indicating whether this instance has potential boundary left over. + /// + /// + /// true if this instance has potential boundary left over; otherwise, false. + /// + public bool HasPotentialBoundaryLeftOver { get; set; } + + /// + /// Gets the boundary delta. + /// + public int BoundaryDelta + { + get { return (_boundaryLength - _boundaryOffset > 0) ? _boundaryLength - _boundaryOffset : _boundaryLength; } + } + + /// + /// Gets or sets the body part. + /// + /// + /// The body part. + /// + public ArraySegment BodyPart { get; set; } = MimeMultipartParser.EmptyBodyPart; + + /// + /// Gets a value indicating whether this body part instance is final. + /// + /// + /// true if this body part instance is final; otherwise, false. + /// + public bool IsFinal { get; private set; } + + /// + /// Resets the boundary offset. + /// + public void ResetBoundaryOffset() + { + _boundaryOffset = _boundaryLength; + } + + /// + /// Resets the boundary. + /// + public void ResetBoundary() + { + // If we had a potential boundary left over then store it so that we don't loose it + if (HasPotentialBoundaryLeftOver) + { + Buffer.BlockCopy(_boundary, 0, _boundaryStore, 0, _boundaryOffset); + _boundaryStoreLength = _boundaryOffset; + HasPotentialBoundaryLeftOver = false; + _releaseDiscardedBoundary = true; + } + + _boundaryLength = 0; + _boundaryOffset = 0; + } + + /// + /// Appends byte to the current boundary. + /// + /// The data to append to the boundary. + public void AppendBoundary(byte data) + { + _boundary[_boundaryLength++] = data; + } + + /// + /// Appends array of bytes to the current boundary. + /// + /// The data to append to the boundary. + /// The offset into the data. + /// The number of bytes to append. + public bool AppendBoundary(byte[] data, int offset, int count) + { + // Check that potential boundary is not bigger than our reference boundary. + // Allow for 2 extra characters to include the final boundary which ends with + // an additional "--" sequence + plus up to 4 LWS characters (which are allowed). + if (_boundaryLength + count > _referenceBoundaryLength + 6) + { + return false; + } + + int cnt = _boundaryLength; + Buffer.BlockCopy(data, offset, _boundary, _boundaryLength, count); + _boundaryLength += count; + + // Verify that boundary matches so far + int maxCount = Math.Min(_boundaryLength, _referenceBoundaryLength); + for (; cnt < maxCount; cnt++) + { + if (_boundary[cnt] != _referenceBoundary[cnt]) + { + return false; + } + } + + return true; + } + + /// + /// Gets the discarded boundary. + /// + /// An containing the discarded boundary. + public ArraySegment GetDiscardedBoundary() + { + if (_boundaryStoreLength > 0 && _releaseDiscardedBoundary) + { + ArraySegment discarded = new ArraySegment(_boundaryStore, 0, _boundaryStoreLength); + _boundaryStoreLength = 0; + return discarded; + } + + return MimeMultipartParser.EmptyBodyPart; + } + + /// + /// Determines whether current boundary is valid. + /// + /// + /// true if curent boundary is valid; otherwise, false. + /// + public bool IsBoundaryValid() + { + int offset = 0; + if (_isFirst) + { + offset = CurrentBodyPartStore.InitialOffset; + } + + int count = offset; + for (; count < _referenceBoundaryLength; count++) + { + if (_boundary[count] != _referenceBoundary[count]) + { + return false; + } + } + + // Check for final + bool boundaryIsFinal = false; + if (_boundary[count] == MimeMultipartParser.Dash && + _boundary[count + 1] == MimeMultipartParser.Dash) + { + boundaryIsFinal = true; + count += 2; + } + + // Rest of boundary must be ignorable whitespace in order for it to match + for (; count < _boundaryLength - 2; count++) + { + if (_boundary[count] != MimeMultipartParser.SP && _boundary[count] != MimeMultipartParser.HTAB) + { + return false; + } + } + + // We have a valid boundary so whatever we stored in the boundary story is no longer needed + IsFinal = boundaryIsFinal; + _isFirst = false; + + return true; + } + + public bool IsBoundaryComplete() + { + if (!IsBoundaryValid()) + { + return false; + } + + if (_boundaryLength < _referenceBoundaryLength) + { + return false; + } + + if (_boundaryLength == _referenceBoundaryLength + 1 && + _boundary[_referenceBoundaryLength] == MimeMultipartParser.Dash) + { + return false; + } + + return true; + } + + /// + /// Clears the body part. + /// + public void ClearBodyPart() + { + BodyPart = MimeMultipartParser.EmptyBodyPart; + } + + /// + /// Clears all. + /// + public void ClearAll() + { + _releaseDiscardedBoundary = false; + HasPotentialBoundaryLeftOver = false; + _boundaryLength = 0; + _boundaryOffset = 0; + _boundaryStoreLength = 0; + IsFinal = false; + ClearBodyPart(); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used for Debugger Display.")] + private string DebuggerToString() + { + var referenceBoundary = Encoding.UTF8.GetString(_referenceBoundary, 0, _referenceBoundaryLength); + var boundary = Encoding.UTF8.GetString(_boundary, 0, _boundaryLength); + + return String.Format( + CultureInfo.InvariantCulture, + "Expected: {0} *** Current: {1}", + referenceBoundary, + boundary); + } + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MultipartExtensions.cs b/src/DicomWebClient/Third-party/Microsoft/MultipartExtensions.cs new file mode 100644 index 000000000..b416e87ed --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MultipartExtensions.cs @@ -0,0 +1,275 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; +using System.IO; +using System.Net.Http.Formatting.Parsers; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; + +namespace System.Net.Http +{ + /// + /// Extension methods to read MIME multipart entities from instances. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class HttpContentMultipartExtensions + { + private const int MinBufferSize = 256; + private const int DefaultBufferSize = 32 * 1024; + + ///// + ///// Determines whether the specified content is MIME multipart content. + ///// + ///// The content. + ///// + ///// true if the specified content is MIME multipart content; otherwise, false. + ///// + //public static bool IsMimeMultipartContent(this HttpContent content) + //{ + // Guard.Against.Null(content, nameof(content)); + + // return MimeMultipartBodyPartParser.IsMimeMultipartContent(content); + //} + + ///// + ///// Determines whether the specified content is MIME multipart content with the + ///// specified subtype. For example, the subtype mixed would match content + ///// with a content type of multipart/mixed. + ///// + ///// The content. + ///// The MIME multipart subtype to match. + ///// + ///// true if the specified content is MIME multipart content with the specified subtype; otherwise, false. + ///// + //public static bool IsMimeMultipartContent(this HttpContent content, string subtype) + //{ + // if (String.IsNullOrWhiteSpace(subtype)) + // { + // throw Error.ArgumentNull("subtype"); + // } + + // if (IsMimeMultipartContent(content)) + // { + // if (content.Headers.ContentType.MediaType.Equals("multipart/" + subtype, StringComparison.OrdinalIgnoreCase)) + // { + // return true; + // } + // } + + // return false; + //} + + /// + /// Reads all body parts within a MIME multipart message into memory using a . + /// + /// An existing instance to use for the object's content. + /// A representing the tasks of getting the result of reading the MIME content. + public static Task ReadAsMultipartAsync(this HttpContent content) + { + return ReadAsMultipartAsync(content, new MultipartMemoryStreamProvider(), DefaultBufferSize); + } + + /// + /// Reads all body parts within a MIME multipart message into memory using a . + /// + /// An existing instance to use for the object's content. + /// The token to monitor for cancellation requests. + /// A representing the tasks of getting the result of reading the MIME content. + public static Task ReadAsMultipartAsync(this HttpContent content, CancellationToken cancellationToken) + { + return ReadAsMultipartAsync(content, new MultipartMemoryStreamProvider(), DefaultBufferSize, cancellationToken); + } + + /// + /// Reads all body parts within a MIME multipart message using the provided instance + /// to determine where the contents of each body part is written. + /// + /// The with which to process the data. + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + /// A representing the tasks of getting the result of reading the MIME content. + public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider) where T : MultipartStreamProvider + { + return ReadAsMultipartAsync(content, streamProvider, DefaultBufferSize); + } + + /// + /// Reads all body parts within a MIME multipart message using the provided instance + /// to determine where the contents of each body part is written. + /// + /// The with which to process the data. + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + /// The token to monitor for cancellation requests. + /// A representing the tasks of getting the result of reading the MIME content. + public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, CancellationToken cancellationToken) + where T : MultipartStreamProvider + { + return ReadAsMultipartAsync(content, streamProvider, DefaultBufferSize, cancellationToken); + } + + /// + /// Reads all body parts within a MIME multipart message using the provided instance + /// to determine where the contents of each body part is written and as read buffer size. + /// + /// The with which to process the data. + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + /// Size of the buffer used to read the contents. + /// A representing the tasks of getting the result of reading the MIME content. + public static Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, int bufferSize) + where T : MultipartStreamProvider + { + return ReadAsMultipartAsync(content, streamProvider, bufferSize, CancellationToken.None); + } + + /// + /// Reads all body parts within a MIME multipart message using the provided instance + /// to determine where the contents of each body part is written and as read buffer size. + /// + /// The with which to process the data. + /// An existing instance to use for the object's content. + /// A stream provider providing output streams for where to write body parts as they are parsed. + /// Size of the buffer used to read the contents. + /// The token to monitor for cancellation requests. + /// A representing the tasks of getting the result of reading the MIME content. + public static async Task ReadAsMultipartAsync(this HttpContent content, T streamProvider, int bufferSize, + CancellationToken cancellationToken) where T : MultipartStreamProvider + { + Guard.Against.Null(content, nameof(content)); + Guard.Against.Null(streamProvider, nameof(streamProvider)); + + if (bufferSize < MinBufferSize) + { + throw new ArgumentOutOfRangeException("bufferSize", $"Value must be greater than or equal to {MinBufferSize}."); + } + + Stream stream; + try + { + stream = await content.ReadAsStreamAsync(); + } + catch (Exception e) + { + throw new IOException("Error reading MIME multipart body part.", e); + } + + using (var parser = new MimeMultipartBodyPartParser(content, streamProvider)) + { + byte[] data = new byte[bufferSize]; + MultipartAsyncContext context = new MultipartAsyncContext(stream, parser, data, streamProvider.Contents); + + // Start async read/write loop + await MultipartReadAsync(context, cancellationToken); + + // Let the stream provider post-process when everything is complete + await streamProvider.ExecutePostProcessingAsync(cancellationToken); + return streamProvider; + } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")] + private static async Task MultipartReadAsync(MultipartAsyncContext context, CancellationToken cancellationToken) + { + Contract.Assert(context != null, "context cannot be null"); + while (true) + { + int bytesRead; + try + { + bytesRead = await context.ContentStream.ReadAsync(context.Data, 0, context.Data.Length, cancellationToken); + } + catch (Exception e) + { + throw new IOException("Error reading MIME multipart body part.", e); + } + + IEnumerable parts = context.MimeParser.ParseBuffer(context.Data, bytesRead); + + foreach (MimeBodyPart part in parts) + { + foreach (ArraySegment segment in part.Segments) + { + try + { + await part.WriteSegment(segment, cancellationToken); + } + catch (Exception e) + { + part.Dispose(); + throw new IOException("Error writing MIME multipart body part to output stream.", e); + } + } + + if (CheckIsFinalPart(part, context.Result)) + { + return; + } + } + } + } + + private static bool CheckIsFinalPart(MimeBodyPart part, ICollection result) + { + Contract.Assert(part != null, "part cannot be null."); + Contract.Assert(result != null, "result cannot be null."); + if (part.IsComplete) + { + HttpContent partContent = part.GetCompletedHttpContent(); + if (partContent != null) + { + result.Add(partContent); + } + + bool isFinal = part.IsFinal; + part.Dispose(); + return isFinal; + } + + return false; + } + + /// + /// Managing state for asynchronous read and write operations + /// + private class MultipartAsyncContext + { + public MultipartAsyncContext(Stream contentStream, MimeMultipartBodyPartParser mimeParser, byte[] data, ICollection result) + { + Contract.Assert(contentStream != null); + Contract.Assert(mimeParser != null); + Contract.Assert(data != null); + + ContentStream = contentStream; + Result = result; + MimeParser = mimeParser; + Data = data; + } + + /// + /// Gets the that we read from. + /// + public Stream ContentStream { get; private set; } + + /// + /// Gets the collection of parsed instances. + /// + public ICollection Result { get; private set; } + + /// + /// The data buffer that we use for reading data from the input stream into before processing. + /// + public byte[] Data { get; private set; } + + /// + /// Gets the MIME parser instance used to parse the data + /// + public MimeMultipartBodyPartParser MimeParser { get; private set; } + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MultipartMemoryStreamProvider.cs b/src/DicomWebClient/Third-party/Microsoft/MultipartMemoryStreamProvider.cs new file mode 100644 index 000000000..dc4892c5d --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MultipartMemoryStreamProvider.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Net.Http.Headers; +using Ardalis.GuardClauses; + +namespace System.Net.Http +{ + /// + /// Provides a implementation that returns a instance. + /// This facilitates deserialization or other manipulation of the contents in memory. + /// + public class MultipartMemoryStreamProvider : MultipartStreamProvider + { + /// + /// This implementation returns a instance. + /// This facilitates deserialization or other manipulation of the contents in memory. + /// + public override Stream GetStream(HttpContent parent, HttpContentHeaders headers) + { + Guard.Against.Null(parent, nameof(parent)); + Guard.Against.Null(headers, nameof(headers)); + + return new MemoryStream(); + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/MultipartStreamProvider.cs b/src/DicomWebClient/Third-party/Microsoft/MultipartStreamProvider.cs new file mode 100644 index 000000000..c3ade19f9 --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/MultipartStreamProvider.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.ObjectModel; +using System.IO; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http +{ + + /// + /// An implementation examines the headers provided by the MIME multipart parser + /// as part of the MIME multipart extension methods (see ) and decides + /// what kind of stream to return for the body part to be written to. + /// + public abstract class MultipartStreamProvider + { + /// + /// Used as the T in a "conversion" of a Task into a Task{T} + /// + private struct AsyncVoid + { + } + + /// + /// Initializes a new instance of the class. + /// + protected MultipartStreamProvider() + { + } + + /// + /// Gets the collection of instances where each instance represents a MIME body part. + /// + public Collection Contents { get; } = new(); + + /// + /// When a MIME multipart body part has been parsed this method is called to get a stream for where to write the body part to. + /// + /// The parent MIME multipart instance. + /// The header fields describing the body parts content. Looking for header fields such as + /// Content-Type and Content-Disposition can help provide the appropriate stream. In addition to using the information + /// in the provided header fields, it is also possible to add new header fields or modify existing header fields. This can + /// be useful to get around situations where the Content-type may say application/octet-stream but based on + /// analyzing the Content-Disposition header field it is found that the content in fact is application/json, for example. + /// A stream instance where the contents of a body part will be written to. + public abstract Stream GetStream(HttpContent parent, HttpContentHeaders headers); + + /// + /// Immediately upon reading the last MIME body part but before completing the read task, this method is + /// called to enable the to do any post processing on the + /// instances that have been read. For example, it can be used to copy the data to another location, or perform + /// some other kind of post processing on the data before completing the read operation. + /// + /// A representing the post processing. + public virtual Task ExecutePostProcessingAsync() + { + return Task.FromResult(default(AsyncVoid)); + } + + /// + /// Immediately upon reading the last MIME body part but before completing the read task, this method is + /// called to enable the to do any post processing on the + /// instances that have been read. For example, it can be used to copy the data to another location, or perform + /// some other kind of post processing on the data before completing the read operation. + /// + /// The token to monitor for cancellation requests. + /// A representing the post processing. + public virtual Task ExecutePostProcessingAsync(CancellationToken cancellationToken) + { + // Call the other overload to maintain backward compatibility. + return ExecutePostProcessingAsync(); + } + } +} diff --git a/src/DicomWebClient/Third-party/Microsoft/README.md b/src/DicomWebClient/Third-party/Microsoft/README.md new file mode 100644 index 000000000..1652d6604 --- /dev/null +++ b/src/DicomWebClient/Third-party/Microsoft/README.md @@ -0,0 +1,8 @@ +All files in this directory sourced from https://github.com/aspnet/AspNetWebStack/tree/1231b77d79956152831b75ad7f094f844251b97f and are needed in order to parse multipart/related content. + +All files in this directory are copyrighted and licensed with the following: + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + diff --git a/src/DicomWebClient/packages.lock.json b/src/DicomWebClient/packages.lock.json index 714d952dd..2e3dd2e40 100644 --- a/src/DicomWebClient/packages.lock.json +++ b/src/DicomWebClient/packages.lock.json @@ -1,117 +1,56 @@ { "version": 1, "dependencies": { - "net6.0": { - "Ardalis.GuardClauses": { - "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } - }, + "net8.0": { "fo-dicom": { "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "requested": "[5.2.1, )", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, - "Microsoft.AspNet.WebApi.Client": { - "type": "Direct", - "requested": "[5.2.9, )", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } - }, - "Microsoft.Extensions.Http": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } - }, - "Microsoft.Net.Http.Headers": { + "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[2.2.8, )", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" - } + "requested": "[8.0.17, )", + "resolved": "8.0.17", + "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw==" }, - "System.Linq.Async": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } + "Ardalis.GuardClauses": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, - "JetBrains.Annotations": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.CSharp": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "P+MBhIM0YX+JqROuf7i306ZLJEjQYA9uUyRDE+OqwUI5sh41e2ZbPQV3LfAPh+29cmceE1pUffXsGfR4eMY3KA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "resolved": "6.0.1", + "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0" @@ -156,360 +95,11 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "TsETIgVJb/AKoYfSP+iCxkuly5d3inZjTdx/ItZLk2CxY85v8083OBS3uai84kK3/baLnS5/b5XGs6zR7SuuHQ==" - }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" - }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, - "Microsoft.Win32.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "NETStandard.Library": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, - "Newtonsoft.Json": { - "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "ebWzW9j2nwxQeBo59As2TYn7nYr9BHicqqCwHOD1Vdo+50HBtLPuqdiCYJcLdTRknpYis/DSEOQz5KmZxwrIAg==", - "dependencies": { - "Microsoft.CSharp": "4.3.0", - "System.Collections": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Runtime.Serialization.Formatters": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - } - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" - }, - "runtime.native.System": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, - "runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" - }, - "System.AppContext": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Collections.NonGeneric": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Collections.Specialized": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.ComponentModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", - "dependencies": { - "System.ComponentModel": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.ComponentModel.TypeConverter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.NonGeneric": "4.3.0", - "System.Collections.Specialized": "4.3.0", - "System.ComponentModel": "4.3.0", - "System.ComponentModel.Primitives": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Console": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", "resolved": "6.0.0", @@ -518,744 +108,35 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.Tools": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.Tracing": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Dynamic.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Globalization": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Calendars": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Globalization.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - } - }, - "System.IO.Compression.ZipFile": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Expressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Net.Sockets": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.ObjectModel": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Reflection": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.ILGeneration": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Emit.Lightweight": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Reflection.TypeExtensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - } - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Runtime.Serialization.Formatters": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "KT591AkTNFOTbhZlaeMVvfax3RqhH1EJlcwF50Wm7sfnBLuHiOeZRRKrr1ns3NESkM20KPZ5Ol/ueMq5vg4QoQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0" - } - }, - "System.Runtime.Serialization.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==", - "dependencies": { - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - } - }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } - }, - "System.Text.Encoding.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "System.Text.RegularExpressions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Threading": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, "System.Threading.Channels": { "type": "Transitive", "resolved": "6.0.0", "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" }, - "System.Threading.Tasks": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Threading.Timer": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Xml.ReaderWriter": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - } - }, - "System.Xml.XDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, - "System.Xml.XmlDocument": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - } - }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } } } diff --git a/src/InformaticsGateway/Common/ApplicationEntityNotFoundException.cs b/src/InformaticsGateway/Common/ApplicationEntityNotFoundException.cs new file mode 100644 index 000000000..9da3b7621 --- /dev/null +++ b/src/InformaticsGateway/Common/ApplicationEntityNotFoundException.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public class ApplicationEntityNotFoundException : Exception + { + public ApplicationEntityNotFoundException() + { + } + + public ApplicationEntityNotFoundException(string message) : base(message) + { + } + + public ApplicationEntityNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Common/DestinationNotSuppliedException.cs b/src/InformaticsGateway/Common/DestinationNotSuppliedException.cs new file mode 100755 index 000000000..4ed6a29e3 --- /dev/null +++ b/src/InformaticsGateway/Common/DestinationNotSuppliedException.cs @@ -0,0 +1,36 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2020 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public class DestinationNotSuppliedException : Exception + { + public DestinationNotSuppliedException() + { + } + + public DestinationNotSuppliedException(string message) : base(message) + { + } + + public DestinationNotSuppliedException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Common/DicomExtensions.cs b/src/InformaticsGateway/Common/DicomExtensions.cs index 958faaadc..df90b7c08 100644 --- a/src/InformaticsGateway/Common/DicomExtensions.cs +++ b/src/InformaticsGateway/Common/DicomExtensions.cs @@ -15,7 +15,6 @@ * limitations under the License. */ -using System; using System.Collections.Generic; using System.Text.Json; using Ardalis.GuardClauses; @@ -35,12 +34,12 @@ public static class DicomExtensions /// /// list of SOP Class UIDs /// Array of DicomTransferSyntax or null if uids is null or empty. - /// Thrown in the specified UID is not a transfer syntax type. + /// Thrown in the specified UID is not a transfer syntax type. public static DicomTransferSyntax[] ToDicomTransferSyntaxArray(this IEnumerable uids) { if (uids.IsNullOrEmpty()) { - return Array.Empty(); + return []; } var dicomTransferSyntaxes = new List(); @@ -49,12 +48,12 @@ public static DicomTransferSyntax[] ToDicomTransferSyntaxArray(this IEnumerable< { dicomTransferSyntaxes.Add(DicomTransferSyntax.Lookup(DicomUID.Parse(uid))); } - return dicomTransferSyntaxes.ToArray(); + return [.. dicomTransferSyntaxes]; } public static string ToJson(this DicomFile dicomFile, DicomJsonOptions dicomJsonOptions, bool validateDicom) { - Guard.Against.Null(dicomFile); + Guard.Against.Null(dicomFile, nameof(dicomFile)); var options = new JsonSerializerOptions(); options.Converters.Add(new DicomJsonConverter( diff --git a/src/InformaticsGateway/Common/DicomToolkit.cs b/src/InformaticsGateway/Common/DicomToolkit.cs index d78455a73..1ba2a872f 100644 --- a/src/InformaticsGateway/Common/DicomToolkit.cs +++ b/src/InformaticsGateway/Common/DicomToolkit.cs @@ -26,14 +26,14 @@ public class DicomToolkit : IDicomToolkit { public Task OpenAsync(Stream stream, FileReadOption fileReadOption = FileReadOption.Default) { - Guard.Against.Null(stream); + Guard.Against.Null(stream, nameof(stream)); return DicomFile.OpenAsync(stream, fileReadOption); } public DicomFile Load(byte[] fileContent) { - Guard.Against.NullOrEmpty(fileContent); + Guard.Against.NullOrEmpty(fileContent, nameof(fileContent)); using var stream = new MemoryStream(fileContent); var dicomFile = DicomFile.Open(stream, FileReadOption.ReadAll); @@ -47,7 +47,7 @@ public DicomFile Load(byte[] fileContent) public StudySerieSopUids GetStudySeriesSopInstanceUids(DicomFile dicomFile) { - Guard.Against.Null(dicomFile); + Guard.Against.Null(dicomFile, nameof(dicomFile)); return new StudySerieSopUids { diff --git a/src/InformaticsGateway/Common/FileMoveException.cs b/src/InformaticsGateway/Common/FileMoveException.cs old mode 100644 new mode 100755 index cb8664c35..55b777d03 --- a/src/InformaticsGateway/Common/FileMoveException.cs +++ b/src/InformaticsGateway/Common/FileMoveException.cs @@ -15,12 +15,10 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] - internal class FileMoveException : Exception + public class FileMoveException : Exception { public FileMoveException() { @@ -38,9 +36,5 @@ public FileMoveException(string source, string destination, Exception ex) : this($"Exception moving file from {source} to {destination}.", ex) { } - - protected FileMoveException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Common/FileStorageMetadataExtensions.cs b/src/InformaticsGateway/Common/FileStorageMetadataExtensions.cs old mode 100644 new mode 100755 index 2ce8b8f0c..4fb328e99 --- a/src/InformaticsGateway/Common/FileStorageMetadataExtensions.cs +++ b/src/InformaticsGateway/Common/FileStorageMetadataExtensions.cs @@ -31,21 +31,22 @@ public static async Task SetDataStreams( DicomFile dicomFile, string dicomJson, TemporaryDataStorageLocation storageLocation, - IFileSystem fileSystem = null, + IFileSystem? fileSystem = null, string temporaryStoragePath = "") { - Guard.Against.Null(dicomFile); - Guard.Against.Null(dicomJson); // allow empty here + Guard.Against.Null(dicomFile, nameof(dicomFile)); + Guard.Against.Null(dicomJson, nameof(dicomJson)); // allow empty here switch (storageLocation) { case TemporaryDataStorageLocation.Disk: - Guard.Against.Null(fileSystem); - Guard.Against.NullOrWhiteSpace(temporaryStoragePath); + Guard.Against.Null(fileSystem, nameof(fileSystem)); + Guard.Against.NullOrWhiteSpace(temporaryStoragePath, nameof(temporaryStoragePath)); var tempFile = fileSystem.Path.Combine(temporaryStoragePath, $@"{fileSystem.Path.GetRandomFileName()}"); dicomFileStorageMetadata.File.Data = fileSystem.File.Create(tempFile); break; + default: dicomFileStorageMetadata.File.Data = new System.IO.MemoryStream(); break; @@ -54,46 +55,47 @@ public static async Task SetDataStreams( await dicomFile.SaveAsync(dicomFileStorageMetadata.File.Data).ConfigureAwait(false); dicomFileStorageMetadata.File.Data.Seek(0, System.IO.SeekOrigin.Begin); - await SetTextStream(dicomFileStorageMetadata.JsonFile, dicomJson, storageLocation, fileSystem, temporaryStoragePath); + await SetTextStream(dicomFileStorageMetadata.JsonFile, dicomJson, storageLocation, fileSystem, temporaryStoragePath).ConfigureAwait(false); } public static async Task SetDataStream( this FhirFileStorageMetadata fhirFileStorageMetadata, string json, TemporaryDataStorageLocation storageLocation, - IFileSystem fileSystem = null, + IFileSystem? fileSystem = null, string temporaryStoragePath = "") - => await SetTextStream(fhirFileStorageMetadata.File, json, storageLocation, fileSystem, temporaryStoragePath); + => await SetTextStream(fhirFileStorageMetadata.File, json, storageLocation, fileSystem, temporaryStoragePath).ConfigureAwait(false); public static async Task SetDataStream( this Hl7FileStorageMetadata hl7FileStorageMetadata, string message, TemporaryDataStorageLocation storageLocation, - IFileSystem fileSystem = null, + IFileSystem? fileSystem = null, string temporaryStoragePath = "") - => await SetTextStream(hl7FileStorageMetadata.File, message, storageLocation, fileSystem, temporaryStoragePath); + => await SetTextStream(hl7FileStorageMetadata.File, message, storageLocation, fileSystem, temporaryStoragePath).ConfigureAwait(false); private static async Task SetTextStream( StorageObjectMetadata storageObjectMetadata, string message, TemporaryDataStorageLocation storageLocation, - IFileSystem fileSystem = null, + IFileSystem? fileSystem = null, string temporaryStoragePath = "") { - Guard.Against.Null(message); // allow empty here + Guard.Against.Null(message, nameof(message)); // allow empty here switch (storageLocation) { case TemporaryDataStorageLocation.Disk: - Guard.Against.Null(fileSystem); - Guard.Against.NullOrWhiteSpace(temporaryStoragePath); + Guard.Against.Null(fileSystem, nameof(fileSystem)); + Guard.Against.NullOrWhiteSpace(temporaryStoragePath, nameof(temporaryStoragePath)); var tempFile = fileSystem.Path.Combine(temporaryStoragePath, $@"{fileSystem.Path.GetRandomFileName()}"); var stream = fileSystem.File.Create(tempFile); var data = Encoding.UTF8.GetBytes(message); - await stream.WriteAsync(data, 0, data.Length); + await stream.WriteAsync(data, 0, data.Length).ConfigureAwait(false); storageObjectMetadata.Data = stream; break; + default: storageObjectMetadata.Data = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(message)); break; diff --git a/src/InformaticsGateway/Common/FileUploadException.cs b/src/InformaticsGateway/Common/FileUploadException.cs index 071ae5171..730a52db7 100644 --- a/src/InformaticsGateway/Common/FileUploadException.cs +++ b/src/InformaticsGateway/Common/FileUploadException.cs @@ -16,11 +16,9 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] public class FileUploadException : Exception { public FileUploadException() @@ -34,9 +32,5 @@ public FileUploadException(string message) : base(message) public FileUploadException(string message, Exception innerException) : base(message, innerException) { } - - protected FileUploadException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Common/IDicomToolkit.cs b/src/InformaticsGateway/Common/IDicomToolkit.cs old mode 100644 new mode 100755 diff --git a/src/InformaticsGateway/Common/InsufficientStorageAvailableException.cs b/src/InformaticsGateway/Common/InsufficientStorageAvailableException.cs old mode 100644 new mode 100755 index 3051b5b3f..467a37d44 --- a/src/InformaticsGateway/Common/InsufficientStorageAvailableException.cs +++ b/src/InformaticsGateway/Common/InsufficientStorageAvailableException.cs @@ -30,7 +30,6 @@ namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] public class InsufficientStorageAvailableException : Exception { public InsufficientStorageAvailableException() @@ -44,10 +43,5 @@ public InsufficientStorageAvailableException(string message) : base(message) public InsufficientStorageAvailableException(string message, Exception innerException) : base(message, innerException) { } - - protected InsufficientStorageAvailableException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) - { - throw new NotImplementedException(); - } } } diff --git a/src/InformaticsGateway/Common/ObjectExistsException.cs b/src/InformaticsGateway/Common/ObjectExistsException.cs old mode 100644 new mode 100755 index 7014ad856..4f5db4bfa --- a/src/InformaticsGateway/Common/ObjectExistsException.cs +++ b/src/InformaticsGateway/Common/ObjectExistsException.cs @@ -16,12 +16,10 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] - internal class ObjectExistsException : Exception + public class ObjectExistsException : Exception { public ObjectExistsException() { @@ -34,9 +32,5 @@ public ObjectExistsException(string message) : base(message) public ObjectExistsException(string message, Exception innerException) : base(message, innerException) { } - - protected ObjectExistsException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } -} \ No newline at end of file +} diff --git a/src/InformaticsGateway/Common/PlugInInitializationException.cs b/src/InformaticsGateway/Common/PlugInInitializationException.cs new file mode 100755 index 000000000..cfd20d5e8 --- /dev/null +++ b/src/InformaticsGateway/Common/PlugInInitializationException.cs @@ -0,0 +1,31 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public class PlugInInitializationException : Exception + { + public PlugInInitializationException(string message) : base(message) + { + } + + public PlugInInitializationException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Common/PlugInLoadingException.cs b/src/InformaticsGateway/Common/PlugInLoadingException.cs new file mode 100644 index 000000000..6deac7d93 --- /dev/null +++ b/src/InformaticsGateway/Common/PlugInLoadingException.cs @@ -0,0 +1,31 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public class PlugInLoadingException : Exception + { + public PlugInLoadingException(string message) : base(message) + { + } + + public PlugInLoadingException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Common/PostPayloadException.cs b/src/InformaticsGateway/Common/PostPayloadException.cs new file mode 100755 index 000000000..aa59081f2 --- /dev/null +++ b/src/InformaticsGateway/Common/PostPayloadException.cs @@ -0,0 +1,45 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public class PostPayloadException : Exception + { + public Payload.PayloadState TargetQueue { get; } + public Payload? Payload { get; } + + public PostPayloadException() + { + } + + public PostPayloadException(Api.Storage.Payload.PayloadState targetState, Payload payload) + { + TargetQueue = targetState; + Payload = payload; + } + + public PostPayloadException(string message) : base(message) + { + } + + public PostPayloadException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Common/ServiceException.cs b/src/InformaticsGateway/Common/ServiceException.cs index ad705a2db..50d392c79 100644 --- a/src/InformaticsGateway/Common/ServiceException.cs +++ b/src/InformaticsGateway/Common/ServiceException.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,9 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] public class ServiceException : Exception { public ServiceException(string message) : base(message) @@ -33,9 +31,5 @@ public ServiceException(string message, Exception innerException) : base(message public ServiceException() { } - - protected ServiceException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Common/ServiceNotFoundException.cs b/src/InformaticsGateway/Common/ServiceNotFoundException.cs index 6668aed50..8cc662f0c 100644 --- a/src/InformaticsGateway/Common/ServiceNotFoundException.cs +++ b/src/InformaticsGateway/Common/ServiceNotFoundException.cs @@ -16,11 +16,9 @@ using System; using System.Globalization; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Common { - [Serializable] public class ServiceNotFoundException : Exception { private static readonly string MessageFormat = "Required service '{0}' cannot be found or cannot be initialized."; @@ -38,9 +36,5 @@ public ServiceNotFoundException(string serviceName, Exception innerException) private ServiceNotFoundException() { } - - protected ServiceNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Common/StudySerieSopUids.cs b/src/InformaticsGateway/Common/StudySerieSopUids.cs old mode 100644 new mode 100755 index 5854caeff..dc89e8692 --- a/src/InformaticsGateway/Common/StudySerieSopUids.cs +++ b/src/InformaticsGateway/Common/StudySerieSopUids.cs @@ -18,10 +18,10 @@ namespace Monai.Deploy.InformaticsGateway.Common { public record StudySerieSopUids { - public string SopClassUid { get; set; } - public string StudyInstanceUid { get; set; } - public string SeriesInstanceUid { get; set; } - public string SopInstanceUid { get; set; } + public string SopClassUid { get; set; } = string.Empty; + public string StudyInstanceUid { get; set; } = string.Empty; + public string SeriesInstanceUid { get; set; } = string.Empty; + public string SopInstanceUid { get; set; } = string.Empty; public string Identifier { get => $"{StudyInstanceUid}/{SeriesInstanceUid}/{SopInstanceUid}"; } } } diff --git a/src/InformaticsGateway/Common/TypeExtensions.cs b/src/InformaticsGateway/Common/TypeExtensions.cs new file mode 100644 index 000000000..d7e52ced3 --- /dev/null +++ b/src/InformaticsGateway/Common/TypeExtensions.cs @@ -0,0 +1,83 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; + +namespace Monai.Deploy.InformaticsGateway.Common +{ + public static class TypeExtensions + { + public static T CreateInstance(this Type type, IServiceProvider serviceProvider, params object[] parameters) + { + Guard.Against.Null(type, nameof(type)); + Guard.Against.Null(serviceProvider, nameof(serviceProvider)); + + return (T)ActivatorUtilities.CreateInstance(serviceProvider, type, parameters); + } + + public static T CreateInstance(this Type interfaceType, IServiceProvider serviceProvider, string typeString, params object[] parameters) + { + Guard.Against.Null(interfaceType, nameof(interfaceType)); + Guard.Against.Null(serviceProvider, nameof(serviceProvider)); + Guard.Against.NullOrWhiteSpace(typeString, nameof(typeString)); + + var type = interfaceType.GetType(typeString); + var processor = ActivatorUtilities.CreateInstance(serviceProvider, type, parameters); + + return (T)processor; + } + + public static Type GetType(this Type interfaceType, string typeString) + { + Guard.Against.Null(interfaceType, nameof(interfaceType)); + Guard.Against.NullOrWhiteSpace(typeString, nameof(typeString)); + + var type = Type.GetType( + typeString, + (name) => + { + var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(z => !string.IsNullOrWhiteSpace(z.FullName) && z.FullName.StartsWith(name.FullName)); + + assembly ??= Assembly.Load(File.ReadAllBytes(Path.Combine(SR.PlugInDirectoryPath, $"{name.Name}.dll"))); + + return assembly; + }, + null, + true); + + if (type is not null && + (type.IsSubclassOf(interfaceType) || + (type.BaseType is not null && type.BaseType.IsAssignableTo(interfaceType)) || + (type.GetInterfaces().Contains(interfaceType)))) + { + return type; + } + + throw new NotSupportedException($"{typeString} is not a sub-type of {interfaceType.Name}"); + } + + public static string GetShortTypeAssemblyName(this Type type) + { + return $"{type.FullName}, {type.Assembly.GetName().Name}"; + } + } +} diff --git a/src/InformaticsGateway/Common/Unsubscriber.cs b/src/InformaticsGateway/Common/Unsubscriber.cs index 45bdc3d6a..650bc7892 100644 --- a/src/InformaticsGateway/Common/Unsubscriber.cs +++ b/src/InformaticsGateway/Common/Unsubscriber.cs @@ -21,7 +21,7 @@ namespace Monai.Deploy.InformaticsGateway.Common { /// - /// Unsubscriber class is used with IObserver for unsubscribing from a notification service. + /// class is used with for unsubscribing from a notification service. /// /// internal class Unsubscriber : IDisposable diff --git a/src/InformaticsGateway/AssemblyInfo.cs b/src/InformaticsGateway/InternalVisibleTo.cs old mode 100644 new mode 100755 similarity index 100% rename from src/InformaticsGateway/AssemblyInfo.cs rename to src/InformaticsGateway/InternalVisibleTo.cs diff --git a/src/InformaticsGateway/Logging/FileLoggingTextFormatter.cs b/src/InformaticsGateway/Logging/FileLoggingTextFormatter.cs deleted file mode 100644 index 15446152f..000000000 --- a/src/InformaticsGateway/Logging/FileLoggingTextFormatter.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021-2022 MONAI Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Globalization; -using System.Text; -using Karambolo.Extensions.Logging.File; -using Microsoft.Extensions.Logging; - -namespace Monai.Deploy.InformaticsGateway.Logging -{ - public class FileLoggingTextFormatter : FileLogEntryTextBuilder - { - public static readonly FileLoggingTextFormatter Default = new(); - - protected override void AppendTimestamp(StringBuilder sb, DateTimeOffset timestamp) - { - sb.Append(timestamp.ToLocalTime().ToString("o", CultureInfo.InvariantCulture)).Append(' '); - } - - protected override void AppendLogScopeInfo(StringBuilder sb, IExternalScopeProvider scopeProvider) - { - scopeProvider.ForEachScope((scope, builder) => - { - builder.Append(' '); - - AppendLogScope(builder, scope!); - }, sb); - } - - protected override void AppendLogScope(StringBuilder sb, object scope) - { - sb.Append('[').Append(scope).Append(']'); - } - - protected override void AppendMessage(StringBuilder sb, string message) - { - sb.Append(" => "); - - var length = sb.Length; - sb.AppendLine(message); - sb.Replace(Environment.NewLine, " ", length, message.Length); - } - - public override void BuildEntryText( - StringBuilder sb, - string categoryName, - LogLevel logLevel, - EventId eventId, - string message, - Exception exception, - IExternalScopeProvider scopeProvider, - DateTimeOffset timestamp) - { - AppendTimestamp(sb, timestamp); - - AppendLogLevel(sb, logLevel); - - AppendCategoryName(sb, categoryName); - - AppendEventId(sb, eventId); - - if (scopeProvider != null) - AppendLogScopeInfo(sb, scopeProvider); - - if (!string.IsNullOrEmpty(message)) - AppendMessage(sb, message); - - if (exception != null) - AppendException(sb, exception); - } - } -} diff --git a/src/InformaticsGateway/Logging/FoDicomLogManager.cs b/src/InformaticsGateway/Logging/FoDicomLogManager.cs deleted file mode 100644 index 4a5fea845..000000000 --- a/src/InformaticsGateway/Logging/FoDicomLogManager.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2022 MONAI Consortium - * Copyright 2019-2021 NVIDIA Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Ardalis.GuardClauses; -using FellowOakDicom.Log; -using Microsoft.Extensions.Logging; - -namespace Monai.Deploy.InformaticsGateway.Logging -{ - public class FoDicomLogManager : LogManager - { - private readonly ILoggerFactory _loggerFactory; - - public FoDicomLogManager(ILoggerFactory loggerFactory) - { - _loggerFactory = loggerFactory ?? throw new System.ArgumentNullException(nameof(loggerFactory)); - } - - protected override Logger GetLoggerImpl(string name) - { - Guard.Against.NullOrWhiteSpace(name); - - return new MicrosoftLoggerAdapter(_loggerFactory.CreateLogger(name)); - } - } -} diff --git a/src/InformaticsGateway/Logging/Log.0.General.cs b/src/InformaticsGateway/Logging/Log.0.General.cs old mode 100644 new mode 100755 index fa610c15e..9fba72e7e --- a/src/InformaticsGateway/Logging/Log.0.General.cs +++ b/src/InformaticsGateway/Logging/Log.0.General.cs @@ -59,5 +59,11 @@ public static partial class Log [LoggerMessage(EventId = 13, Level = LogLevel.Critical, Message = "Failed to start {ServiceName}.")] public static partial void ServiceFailedToStart(this ILogger logger, string serviceName, Exception ex); + + [LoggerMessage(EventId = 14, Level = LogLevel.Critical, Message = "All services are unhealthy")] + public static partial void AllServiceUnheathly(this ILogger logger); + + [LoggerMessage(EventId = 15, Level = LogLevel.Error, Message = "Some services are unhealthy {list}")] + public static partial void SomeServiceUnheathly(this ILogger logger, string list); } } diff --git a/src/InformaticsGateway/Logging/Log.100.200.ScpService.cs b/src/InformaticsGateway/Logging/Log.100.200.ScpService.cs old mode 100644 new mode 100755 index 4e5aee79b..d3c420f1f --- a/src/InformaticsGateway/Logging/Log.100.200.ScpService.cs +++ b/src/InformaticsGateway/Logging/Log.100.200.ScpService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,15 +58,16 @@ public static partial class Log [LoggerMessage(EventId = 112, Level = LogLevel.Information, Message = "Notifying {count} observers of MONAI Application Entity {eventType}.")] public static partial void NotifyAeChanged(this ILogger logger, int count, ChangedEventType eventType); + [LoggerMessage(EventId = 113, Level = LogLevel.Error, Message = "Failed to update Application Entity Handler with updated AE Title {aeTitle}.")] + public static partial void FailedToUpdateAppliationEntityHandlerWithUpdatedAEChange(this ILogger logger, string aeTitle, Exception? ex = null); + // SCP Service - [LoggerMessage(EventId = 200, Level = LogLevel.Information, Message = "Initializing SCP Service at port {port}...")] - public static partial void ScpServiceLoading(this ILogger logger, int port); [LoggerMessage(EventId = 201, Level = LogLevel.Critical, Message = "Failed to initialize SCP listener.")] public static partial void ScpListenerInitializationFailure(this ILogger logger); - [LoggerMessage(EventId = 202, Level = LogLevel.Information, Message = "SCP listening on port: {port}.")] - public static partial void ScpListeningOnPort(this ILogger logger, int port); + [LoggerMessage(EventId = 202, Level = LogLevel.Information, Message = "{serviceName} listening on port: {port}.")] + public static partial void ScpListeningOnPort(this ILogger logger, string serviceName, int port); [LoggerMessage(EventId = 203, Level = LogLevel.Information, Message = "C-ECHO request received.")] public static partial void CEchoReceived(this ILogger logger); @@ -97,5 +98,14 @@ public static partial class Log [LoggerMessage(EventId = 212, Level = LogLevel.Error, Message = "Failed to process C-STORE request, out of storage space.")] public static partial void CStoreFailedDueToLowStorageSpace(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 213, Level = LogLevel.Error, Message = "Error saving DICOM association information. Correlation ID={correlationId}.")] + public static partial void ErrorSavingDicomAssociationInfo(this ILogger logger, Guid correlationId, Exception ex); + + [LoggerMessage(EventId = 214, Level = LogLevel.Information, Message = "Connection closed. Correlation ID={correlationId}. Calling AE Title={callingAeTitle}. Called AE Title={calledAeTitle}. Duration={durationSeconds} seconds.")] + public static partial void ConnectionClosed(this ILogger logger, string correlationId, string callingAeTitle, string calledAeTitle, double durationSeconds); + + [LoggerMessage(EventId = 215, Level = LogLevel.Warning, Message = "Failed to find stored external app details for studyInstance Uid {studyInstanceUid}.")] + public static partial void FailedToFindStoredExtAppDetails(this ILogger logger, string studyInstanceUid); } } diff --git a/src/InformaticsGateway/Logging/Log.2000.InferenceRequestRepository.cs b/src/InformaticsGateway/Logging/Log.2000.InferenceRequestRepository.cs index ec28a1a8d..96b03a6d0 100644 --- a/src/InformaticsGateway/Logging/Log.2000.InferenceRequestRepository.cs +++ b/src/InformaticsGateway/Logging/Log.2000.InferenceRequestRepository.cs @@ -14,13 +14,9 @@ * limitations under the License. */ -using System; -using Microsoft.Extensions.Logging; - namespace Monai.Deploy.InformaticsGateway.Logging { public static partial class Log { - } } diff --git a/src/InformaticsGateway/Logging/Log.3000.PayloadAssembler.cs b/src/InformaticsGateway/Logging/Log.3000.PayloadAssembler.cs old mode 100644 new mode 100755 index ea27adfe0..e1ee29d93 --- a/src/InformaticsGateway/Logging/Log.3000.PayloadAssembler.cs +++ b/src/InformaticsGateway/Logging/Log.3000.PayloadAssembler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,17 +24,17 @@ public static partial class Log [LoggerMessage(EventId = 3000, Level = LogLevel.Information, Message = "[Startup] Removing payloads from database.")] public static partial void RemovingPendingPayloads(this ILogger logger); - [LoggerMessage(EventId = 3002, Level = LogLevel.Information, Message = "[Startup] {count} payloads restored from database.")] + [LoggerMessage(EventId = 3002, Level = LogLevel.Information, Message = "[Startup] {count} pending payloads removed from database.")] public static partial void TotalNumberOfPayloadsRemoved(this ILogger logger, int count); - [LoggerMessage(EventId = 3003, Level = LogLevel.Information, Message = "File added to bucket {key}. Queue size: {count}")] - public static partial void FileAddedToBucket(this ILogger logger, string key, int count); + [LoggerMessage(EventId = 3003, Level = LogLevel.Information, Message = "File added to bucket {key}. Queue size: {count}. PayloadId {PayloadId}")] + public static partial void FileAddedToBucket(this ILogger logger, string key, int count, string PayloadId); [LoggerMessage(EventId = 3004, Level = LogLevel.Trace, Message = "Number of incomplete payloads waiting for processing: {count}.")] public static partial void BucketsActive(this ILogger logger, int count); - [LoggerMessage(EventId = 3005, Level = LogLevel.Trace, Message = "Checking elapsed time for bucket: {key}.")] - public static partial void BucketElapsedTime(this ILogger logger, string key); + [LoggerMessage(EventId = 3005, Level = LogLevel.Trace, Message = "Checking elapsed time for bucket: {key} with timeout set to {timeout}s. Elapsed {elapsed}s with {succeededFiles} uplaoded and {failedFiles} failures out of {totalNumberOfFiles}.")] + public static partial void BucketElapsedTime(this ILogger logger, string key, uint timeout, double elapsed, int totalNumberOfFiles, int succeededFiles, int failedFiles); [LoggerMessage(EventId = 3007, Level = LogLevel.Information, Message = "Bucket {key} sent to processing queue with {count} files.")] public static partial void BucketReady(this ILogger logger, string key, int count); @@ -54,7 +54,10 @@ public static partial class Log [LoggerMessage(EventId = 3012, Level = LogLevel.Information, Message = "Bucket {key} created with timeout {timeout}s.")] public static partial void BucketCreated(this ILogger logger, string key, uint timeout); - [LoggerMessage(EventId = 3014, Level = LogLevel.Error, Message = "Payload deleted due to upload failure(s) {key}.")] - public static partial void PayloadRemovedWithFailureUploads(this ILogger logger, string key); + [LoggerMessage(EventId = 3014, Level = LogLevel.Error, Message = "Payload ({key}) with {totalNumberOfFiles} files deleted due to {failures} upload failure(s).")] + public static partial void PayloadRemovedWithFailureUploads(this ILogger logger, string key, int totalNumberOfFiles, int failures); + + [LoggerMessage(EventId = 3015, Level = LogLevel.Information, Message = "Receieved a payload with {totalNumberOfFiles} files and {failedFiles} failures in {elapsed} seconds.")] + public static partial void ReceievedAPayload(this ILogger logger, double elapsed, int totalNumberOfFiles, int failedFiles); } } diff --git a/src/InformaticsGateway/Logging/Log.4000.ObjectUploadService.cs b/src/InformaticsGateway/Logging/Log.4000.ObjectUploadService.cs old mode 100644 new mode 100755 index 316e941bd..8939da80e --- a/src/InformaticsGateway/Logging/Log.4000.ObjectUploadService.cs +++ b/src/InformaticsGateway/Logging/Log.4000.ObjectUploadService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,14 +21,14 @@ namespace Monai.Deploy.InformaticsGateway.Logging { public static partial class Log { - [LoggerMessage(EventId = 4000, Level = LogLevel.Warning, Message = "Failed to upload file {identifier}; added back to queue for retry.")] - public static partial void FailedToUploadFile(this ILogger logger, string identifier, Exception ex); + [LoggerMessage(EventId = 4000, Level = LogLevel.Warning, Message = "Failed to upload file {identifier}; path: {path} added back to queue for retry.")] + public static partial void FailedToUploadFile(this ILogger logger, string identifier, string path, Exception ex); [LoggerMessage(EventId = 4001, Level = LogLevel.Debug, Message = "Upload statistics: {threads} threads, {seconds} seconds.")] public static partial void UploadStats(this ILogger logger, int threads, double seconds); - [LoggerMessage(EventId = 4002, Level = LogLevel.Debug, Message = "Uploading file to temporary store at {filePath}.")] - public static partial void UploadingFileToTemporaryStore(this ILogger logger, string filePath); + [LoggerMessage(EventId = 4002, Level = LogLevel.Debug, Message = "Uploading file to storeage at {filePath}.")] + public static partial void UploadingFileToStoreage(this ILogger logger, string filePath); [LoggerMessage(EventId = 4003, Level = LogLevel.Information, Message = "Instance queued for upload {identifier}. Items in queue {count} using memory {memoryUsageKb}KB.")] public static partial void InstanceAddedToUploadQueue(this ILogger logger, string identifier, int count, double memoryUsageKb); @@ -36,11 +36,11 @@ public static partial class Log [LoggerMessage(EventId = 4004, Level = LogLevel.Debug, Message = "Error removing objects that are pending upload during startup.")] public static partial void ErrorRemovingPendingUploadObjects(this ILogger logger, Exception ex); - [LoggerMessage(EventId = 4005, Level = LogLevel.Debug, Message = "Error uploading temporary store. Waiting {timeSpan} before next retry. Retry attempt {retryCount}.")] + [LoggerMessage(EventId = 4005, Level = LogLevel.Error, Message = "Error uploading storeage. Waiting {timeSpan} before next retry. Retry attempt {retryCount}.")] public static partial void ErrorUploadingFileToTemporaryStore(this ILogger logger, TimeSpan timespan, int retryCount, Exception ex); - [LoggerMessage(EventId = 4006, Level = LogLevel.Information, Message = "File uploaded to temporary store at {filePath}.")] - public static partial void UploadedFileToTemporaryStore(this ILogger logger, string filePath); + [LoggerMessage(EventId = 4006, Level = LogLevel.Information, Message = "File uploaded to storeage at {filePath}.")] + public static partial void UploadedFileToStoreage(this ILogger logger, string filePath); [LoggerMessage(EventId = 4007, Level = LogLevel.Debug, Message = "Items in queue {count}.")] public static partial void InstanceInUploadQueue(this ILogger logger, int count); @@ -53,5 +53,8 @@ public static partial class Log [LoggerMessage(EventId = 4010, Level = LogLevel.Debug, Message = "File {path} exists={exists}.")] public static partial void VerifyFileExists(this ILogger logger, string path, bool exists); + + [LoggerMessage(EventId = 4011, Level = LogLevel.Information, Message = "Initializing Object Uploser service with {threads} workers.")] + public static partial void InitializeThreads(this ILogger logger, int threads); } } diff --git a/src/InformaticsGateway/Logging/Log.500.ExportService.cs b/src/InformaticsGateway/Logging/Log.500.ExportService.cs old mode 100644 new mode 100755 index 94f3c6808..3fb1ff065 --- a/src/InformaticsGateway/Logging/Log.500.ExportService.cs +++ b/src/InformaticsGateway/Logging/Log.500.ExportService.cs @@ -37,8 +37,8 @@ public static partial class Log [LoggerMessage(EventId = 504, Level = LogLevel.Debug, Message = "File {file} ready for export.")] public static partial void FileReadyForExport(this ILogger logger, string file); - [LoggerMessage(EventId = 505, Level = LogLevel.Information, Message = "Export task completed with {failedCount} failures out of {fileCount}.")] - public static partial void ExportCompleted(this ILogger logger, int failedCount, int fileCount); + [LoggerMessage(EventId = 505, Level = LogLevel.Information, Message = "Export task completed with {failedCount} failures out of {fileCount}. Time to export: {durationMilliseconds}ms.")] + public static partial void ExportCompleted(this ILogger logger, int failedCount, int fileCount, double durationMilliseconds); [LoggerMessage(EventId = 506, Level = LogLevel.Error, Message = "Error downloading payload. Waiting {timeSpan} before next retry. Retry attempt {retryCount}.")] public static partial void ErrorDownloadingPayloadWithRetry(this ILogger logger, Exception ex, TimeSpan timeSpan, int retryCount); @@ -118,10 +118,25 @@ public static partial class Log [LoggerMessage(EventId = 531, Level = LogLevel.Warning, Message = "Export service paused due to insufficient storage space. Available storage space: {availableFreeSpace:D}")] public static partial void ExportServiceStoppedDueToLowStorageSpace(this ILogger logger, long availableFreeSpace); - [LoggerMessage(EventId = 532, Level = LogLevel.Information, Message = "CorrelationId={correlationId}. Export request {exportTaskId} received & queued for processing.")] - public static partial void ExportRequestQueuedForProcessing(this ILogger logger, string correlationId, string exportTaskId); + [LoggerMessage(EventId = 532, Level = LogLevel.Information, Message = "CorrelationId={correlationId}. MessageId={messageId}. Export request {exportTaskId} received & queued for processing.")] + public static partial void ExportRequestQueuedForProcessing(this ILogger logger, string correlationId, string exportTaskId, string messageId); [LoggerMessage(EventId = 533, Level = LogLevel.Error, Message = "Recovering messaging service connection due to {reason}.")] public static partial void MessagingServiceErrorRecover(this ILogger logger, string reason); + + [LoggerMessage(EventId = 534, Level = LogLevel.Error, Message = "Error posting export job for processing correlation ID {correlationId}, export task ID {exportTaskId}.")] + public static partial void ErrorPostingExportJobToQueue(this ILogger logger, string correlationId, string exportTaskId); + + [LoggerMessage(EventId = 535, Level = LogLevel.Warning, Message = "Exceeded maximum number of worker in {serviceName}: {count}.")] + public static partial void ExceededMaxmimumNumberOfWorkers(this ILogger logger, string serviceName, ulong count); + + [LoggerMessage(EventId = 536, Level = LogLevel.Error, Message = "Error executing data plug-ins.")] + public static partial void ErrorExecutingDataPlugIns(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 537, Level = LogLevel.Error, Message = "Error executing OutputDataEngine plug-in.")] + public static partial void OutputDataEngineBlockException(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 538, Level = LogLevel.Error, Message = "Error exporting to Hl7 destination. Waiting {timeSpan} before next retry. Retry attempt {retryCount}.")] + public static partial void HL7ExportErrorWithRetry(this ILogger logger, TimeSpan timespan, int retryCount, Exception ex); } } diff --git a/src/InformaticsGateway/Logging/Log.5000.DataPlugins.cs b/src/InformaticsGateway/Logging/Log.5000.DataPlugins.cs new file mode 100755 index 000000000..ecf6589d3 --- /dev/null +++ b/src/InformaticsGateway/Logging/Log.5000.DataPlugins.cs @@ -0,0 +1,52 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using Microsoft.Extensions.Logging; + +namespace Monai.Deploy.InformaticsGateway.Logging +{ + public static partial class Log + { + [LoggerMessage(EventId = 5000, Level = LogLevel.Information, Message = "Loading assembly from {filename}.")] + public static partial void LoadingAssembly(this ILogger logger, string filename); + + [LoggerMessage(EventId = 5001, Level = LogLevel.Information, Message = "{type} data plug-in found {name}: {plugin}.")] + public static partial void DataPlugInFound(this ILogger logger, string type, string name, string plugin); + + [LoggerMessage(EventId = 5002, Level = LogLevel.Debug, Message = "Adding input data plug-in: {plugin}.")] + public static partial void AddingInputDataPlugIn(this ILogger logger, string plugin); + + [LoggerMessage(EventId = 5003, Level = LogLevel.Information, Message = "Executing input data plug-in: {plugin}.")] + public static partial void ExecutingInputDataPlugIn(this ILogger logger, string plugin); + + [LoggerMessage(EventId = 5004, Level = LogLevel.Debug, Message = "Adding output data plug-in: {plugin}.")] + public static partial void AddingOutputDataPlugIn(this ILogger logger, string plugin); + + [LoggerMessage(EventId = 5005, Level = LogLevel.Information, Message = "Executing output data plug-in: {plugin}.")] + public static partial void ExecutingOutputDataPlugIn(this ILogger logger, string plugin); + + [LoggerMessage(EventId = 5006, Level = LogLevel.Debug, Message = "Adding SCP Listener {serviceName} on port {port}")] + public static partial void AddingScpListener(this ILogger logger, string serviceName, int port); + + [LoggerMessage(EventId = 5007, Level = LogLevel.Error, Message = "Error executing plug-in: {plugin}.")] + public static partial void ErrorAddingOutputDataPlugIn(this ILogger logger, Exception d, string plugin); + + [LoggerMessage(EventId = 5008, Level = LogLevel.Trace, Message = "InputDataPlugInEngine: {pluginName} executed. fileMetadata now: {fileMetadata}")] + public static partial void ExecutedInputDataPlugIn(this ILogger logger, string pluginName, string fileMetadata); + + } +} diff --git a/src/InformaticsGateway/Logging/Log.600.DataRetrievalService.cs b/src/InformaticsGateway/Logging/Log.600.DataRetrievalService.cs old mode 100644 new mode 100755 index a1f51528b..aac8d97dd --- a/src/InformaticsGateway/Logging/Log.600.DataRetrievalService.cs +++ b/src/InformaticsGateway/Logging/Log.600.DataRetrievalService.cs @@ -36,7 +36,7 @@ public static partial class Log public static partial void RestoredFile(this ILogger logger, string file); [LoggerMessage(EventId = 604, Level = LogLevel.Error, Message = "Error retrieving FHIR resource {type}/{id}.")] - public static partial void ErrorRetrievingFhirResource(this ILogger logger, string type, string id, Exception ex); + public static partial void ErrorRetrievingFhirResource(this ILogger logger, string? type, string? id, Exception ex); [LoggerMessage(EventId = 605, Level = LogLevel.Error, Message = "Failed to retrieve resource {type}/{id} with status code {statusCode}, retry count={retryCount}.")] public static partial void ErrorRetrievingFhirResourceWithRetry(this ILogger logger, string type, string id, HttpStatusCode statusCode, int retryCount, Exception ex); diff --git a/src/InformaticsGateway/Logging/Log.700.PayloadService.cs b/src/InformaticsGateway/Logging/Log.700.PayloadService.cs old mode 100644 new mode 100755 index b37c56fa7..37b1e5cc0 --- a/src/InformaticsGateway/Logging/Log.700.PayloadService.cs +++ b/src/InformaticsGateway/Logging/Log.700.PayloadService.cs @@ -53,8 +53,8 @@ public static partial class Log [LoggerMessage(EventId = 711, Level = LogLevel.Information, Message = "Publishing workflow request message ID={messageId}...")] public static partial void PublishingWorkflowRequest(this ILogger logger, string messageId); - [LoggerMessage(EventId = 712, Level = LogLevel.Information, Message = "Workflow request published to {queue}, message ID={messageId}. Payload took {payloadElapsedTime} to complete.")] - public static partial void WorkflowRequestPublished(this ILogger logger, string queue, string messageId, TimeSpan payloadElapsedTime); + [LoggerMessage(EventId = 712, Level = LogLevel.Information, Message = "Workflow request published to {queue}, message ID={messageId}. Payload took {durationSeconds} seconds to complete.")] + public static partial void WorkflowRequestPublished(this ILogger logger, string queue, string messageId, double durationSeconds); [LoggerMessage(EventId = 713, Level = LogLevel.Information, Message = "Restoring payloads from database.")] public static partial void StartupRestoreFromDatabase(this ILogger logger); @@ -116,13 +116,55 @@ public static partial class Log [LoggerMessage(EventId = 736, Level = LogLevel.Debug, Message = "Failed to delete temporary file {identifier} from temporary bucket {bucket} at {remotePath}.")] public static partial void ErrorDeletingFileAfterMoveComplete(this ILogger logger, string bucket, string identifier, string remotePath); - [LoggerMessage(EventId = 737, Level = LogLevel.Trace, Message = "File found on storage service {bucket}: {filePath}.")] - public static partial void FileFounddOnStorageService(this ILogger logger, string bucket, string filePath); + [LoggerMessage(EventId = 737, Level = LogLevel.Trace, Message = "File found on storage service {bucket}: {filePaths}.")] + public static partial void FileFounddOnStorageService(this ILogger logger, string bucket, string filePaths); [LoggerMessage(EventId = 738, Level = LogLevel.Error, Message = "Error listing files on storage service.")] public static partial void ErrorListingFilesOnStorageService(this ILogger logger, Exception ex); [LoggerMessage(EventId = 739, Level = LogLevel.Trace, Message = "Total number of files found on storage service {bucket}: {count}.")] public static partial void FilesFounddOnStorageService(this ILogger logger, string bucket, int count); + + [LoggerMessage(EventId = 740, Level = LogLevel.Error, Message = "Some or all files were missing in payload {payloadId}, will abort the request.")] + public static partial void DeletePayloadDueToMissingFiles(this ILogger logger, Guid payloadId, Exception ex); + + [LoggerMessage(EventId = 741, Level = LogLevel.Error, Message = "File {file} not found in {payloadId}.")] + public static partial void FileMissingInPayload(this ILogger logger, Guid payloadId, string file, Exception ex); + + [LoggerMessage(EventId = 742, Level = LogLevel.Critical, Message = "Storage service connection error.")] + public static partial void StorageServiceConnectionError(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 743, Level = LogLevel.Error, Message = "Exception moving payload.")] + public static partial void PayloadMoveException(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 744, Level = LogLevel.Warning, Message = "PayloadNotification move payload queue: faulted: {isFauled}, cancelled: {isCancelled}.")] + public static partial void MoveQueueFaulted(this ILogger logger, bool isFauled, bool isCancelled); + + [LoggerMessage(EventId = 745, Level = LogLevel.Warning, Message = "PayloadNotification publishing payload queue: faulted: {isFauled}, cancelled: {isCancelled}.")] + public static partial void PublishQueueFaulted(this ILogger logger, bool isFauled, bool isCancelled); + + [LoggerMessage(EventId = 746, Level = LogLevel.Error, Message = "Error posting payload to move queue.")] + public static partial void ErrorPostingJobToMovePayloadsQueue(this ILogger logger); + + [LoggerMessage(EventId = 747, Level = LogLevel.Error, Message = "Error posting payload to publish queue.")] + public static partial void ErrorPostingJobToPublishPayloadsQueue(this ILogger logger); + + [LoggerMessage(EventId = 748, Level = LogLevel.Debug, Message = "Generating artifact recieeved request message for payload {payloadId}...")] + public static partial void GenerateArtifactReceievedRequest(this ILogger logger, Guid payloadId); + + [LoggerMessage(EventId = 749, Level = LogLevel.Information, Message = "Publishing artifact recieved request message ID={messageId}...")] + public static partial void PublishingArtifactRecievedRequest(this ILogger logger, string messageId); + + [LoggerMessage(EventId = 750, Level = LogLevel.Information, Message = "Artifact recieved published to {queue}, message ID={messageId}. Payload took {durationSeconds} seconds to complete.")] + public static partial void ArtifactRecievedPublished(this ILogger logger, string queue, string messageId, double durationSeconds); + + [LoggerMessage(EventId = 751, Level = LogLevel.Debug, Message = "Saving External App Data to repo for {studyInstanceUID}.")] + public static partial void SavingExternalAppData(this ILogger logger, string studyInstanceUID); + + [LoggerMessage(EventId = 752, Level = LogLevel.Debug, Message = "Payload in Notification handler {payloadId}.")] + public static partial void PayloadNotifyAsync(this ILogger logger, string payloadId); + + [LoggerMessage(EventId = 753, Level = LogLevel.Trace, Message = "Adding files to ArtifactsReceivedEvent files {payload}")] + public static partial void AddingFilesToArtifactsReceivedEvent(this ILogger logger, string payload); } } diff --git a/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs b/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs old mode 100644 new mode 100755 index 419060787..bc7ef1cdd --- a/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs +++ b/src/InformaticsGateway/Logging/Log.800.Hl7Service.cs @@ -51,14 +51,14 @@ public static partial class Log [LoggerMessage(EventId = 809, Level = LogLevel.Debug, Message = "Acknowledgment type={value}.")] public static partial void AcknowledgmentType(this ILogger logger, string value); - [LoggerMessage(EventId = 810, Level = LogLevel.Information, Message = "Acknowledgment sent: length={length}.")] - public static partial void AcknowledgmentSent(this ILogger logger, int length); + [LoggerMessage(EventId = 810, Level = LogLevel.Information, Message = "Acknowledgment sent message:{message} length:{length}.")] + public static partial void AcknowledgmentSent(this ILogger logger, string message, int length); [LoggerMessage(EventId = 811, Level = LogLevel.Debug, Message = "HL7 bytes received: {length}.")] public static partial void Hl7MessageBytesRead(this ILogger logger, int length); - [LoggerMessage(EventId = 812, Level = LogLevel.Debug, Message = "Parsing message with {length} bytes.")] - public static partial void Hl7GenerateMessage(this ILogger logger, int length); + [LoggerMessage(EventId = 812, Level = LogLevel.Debug, Message = "Parsing message with {length} bytes. {message}")] + public static partial void Hl7GenerateMessage(this ILogger logger, int length, string message); [LoggerMessage(EventId = 813, Level = LogLevel.Debug, Message = "Waiting for HL7 message.")] public static partial void HL7ReadingMessage(this ILogger logger); @@ -68,5 +68,54 @@ public static partial class Log [LoggerMessage(EventId = 815, Level = LogLevel.Information, Message = "HL7 client {clientId} disconnected.")] public static partial void Hl7ClientRemoved(this ILogger logger, Guid clientId); + + [LoggerMessage(EventId = 816, Level = LogLevel.Debug, Message = "HL7 config loaded. {config}")] + public static partial void Hl7ConfigLoaded(this ILogger logger, string config); + + [LoggerMessage(EventId = 817, Level = LogLevel.Information, Message = "No HL7 config found")] + public static partial void Hl7NoConfig(this ILogger logger); + + [LoggerMessage(EventId = 818, Level = LogLevel.Debug, Message = "HL7 no matching config found for message {message}")] + public static partial void Hl7NoMatchingConfig(this ILogger logger, string message); + + [LoggerMessage(EventId = 819, Level = LogLevel.Debug, Message = "HL7 found matching config found for. {Id} config: {config}")] + public static partial void Hl7FoundMatchingConfig(this ILogger logger, string Id, string config); + + [LoggerMessage(EventId = 820, Level = LogLevel.Warning, Message = "HL7 exception thrown extracting Hl7 Info")] + public static partial void Hl7ExceptionThrow(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 821, Level = LogLevel.Warning, Message = "HL7 external App Details not found")] + public static partial void Hl7ExtAppDetailsNotFound(this ILogger logger); + + [LoggerMessage(EventId = 822, Level = LogLevel.Debug, Message = "HL7 changing value {hl7Tag} from {oldValue} to {newValue}")] + public static partial void ChangingHl7Values(this ILogger logger, string hl7Tag, string oldValue, string newValue); + + [LoggerMessage(EventId = 823, Level = LogLevel.Error, Message = "HL7 destination stream not writable")] + public static partial void Hl7ClientStreamNotWritable(this ILogger logger); + + [LoggerMessage(EventId = 824, Level = LogLevel.Error, Message = "HL7 Ack missing start or end characters")] + public static partial void Hl7AckMissingStartOrEndCharacters(this ILogger logger); + + [LoggerMessage(EventId = 825, Level = LogLevel.Error, Message = "HL7 Execption sending Hl7 meassage")] + public static partial void Hl7SendException(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 826, Level = LogLevel.Debug, Message = "HL7 meassage sent received {ack}")] + public static partial void Hl7MessageSent(this ILogger logger, string ack); + + [LoggerMessage(EventId = 827, Level = LogLevel.Warning, Message = "HL7 plugin loading exceptions")] + public static partial void HL7PluginLoadingExceptions(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 828, Level = LogLevel.Information, Message = "HL7 message recieved. {message}")] + public static partial void Hl7MessageReceieved(this ILogger logger, string message); + + [LoggerMessage(EventId = 829, Level = LogLevel.Trace, Message = "HL7 config Not matching message Id {senderId} configId {configID}")] + public static partial void Hl7NotMatchingConfig(this ILogger logger, string senderId, string configID); + + [LoggerMessage(EventId = 830, Level = LogLevel.Error, Message = "Error generating HL7 acknowledgment. for message {message}")] + public static partial void ErrorGeneratingHl7Acknowledgment(this ILogger logger, Exception ex, string message); + + [LoggerMessage(EventId = 831, Level = LogLevel.Trace, Message = "HL7 message after plug-in processing: {message} correlationId: {CorrelationId}")] + public static partial void HL7MessageAfterPluginProcessing(this ILogger logger, string message, string CorrelationId); + } } diff --git a/src/InformaticsGateway/Logging/Log.8000.HttpServices.cs b/src/InformaticsGateway/Logging/Log.8000.HttpServices.cs old mode 100644 new mode 100755 index b52f0eb9e..bd706d8ce --- a/src/InformaticsGateway/Logging/Log.8000.HttpServices.cs +++ b/src/InformaticsGateway/Logging/Log.8000.HttpServices.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,12 @@ public static partial class Log [LoggerMessage(EventId = 8004, Level = LogLevel.Information, Message = "MONAI SCP Application Entity deleted {name}.")] public static partial void MonaiApplicationEntityDeleted(this ILogger logger, string name); + [LoggerMessage(EventId = 8005, Level = LogLevel.Information, Message = "MONAI SCP AE Title {name} updated AE Title={aeTitle}.")] + public static partial void MonaiApplicationEntityUpdated(this ILogger logger, string name, string aeTitle); + + [LoggerMessage(EventId = 8006, Level = LogLevel.Error, Message = "Error reading data input plug-ins.")] + public static partial void ErrorReadingDataInputPlugIns(this ILogger logger, Exception ex); + // Destination AE Title Controller [LoggerMessage(EventId = 8010, Level = LogLevel.Information, Message = "DICOM destination added AE Title={aeTitle}, Host/IP={hostIp}.")] public static partial void DestinationApplicationEntityAdded(this ILogger logger, string aeTitle, string hostIp); @@ -94,16 +100,35 @@ public static partial class Log [LoggerMessage(EventId = 8040, Level = LogLevel.Error, Message = "Error collecting system status.")] public static partial void ErrorCollectingSystemStatus(this ILogger logger, Exception ex); + // Virtual AE Title Controller + [LoggerMessage(EventId = 8050, Level = LogLevel.Error, Message = "Error querying Virtual Application Entity.")] + public static partial void ErrorListingVirtualApplicationEntities(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8051, Level = LogLevel.Error, Message = "Error adding Virtual Application Entity.")] + public static partial void ErrorAddingVirtualApplicationEntity(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8052, Level = LogLevel.Error, Message = "Error deleting Virtual Application Entity.")] + public static partial void ErrorDeletingVirtualApplicationEntity(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8053, Level = LogLevel.Information, Message = "Virtual SCP AE Title added AE Title={aeTitle}.")] + public static partial void VirtualApplicationEntityAdded(this ILogger logger, string aeTitle); + + [LoggerMessage(EventId = 8054, Level = LogLevel.Information, Message = "MONAI SCP Application Entity deleted {name}.")] + public static partial void VirtualApplicationEntityDeleted(this ILogger logger, string name); + + [LoggerMessage(EventId = 8055, Level = LogLevel.Information, Message = "Virtual SCP AE Title {name} updated AE Title={aeTitle}.")] + public static partial void VirtualApplicationEntityUpdated(this ILogger logger, string name, string aeTitle); + // Middleware [LoggerMessage(EventId = 8090, Level = LogLevel.Error, Message = "HTTP error in request {path}.")] public static partial void HttpRequestError(this ILogger logger, string path, Exception ex); // DicomWeb STOW [LoggerMessage(EventId = 8100, Level = LogLevel.Error, Message = "Error processing DICOMWeb STOW StudyInstanceUID= {studyInstanceUid}, Workflow={workflowName}.")] - public static partial void ErrorDicomWebStow(this ILogger logger, string studyInstanceUid, string workflowName, Exception ex); + public static partial void ErrorDicomWebStow(this ILogger logger, string? studyInstanceUid, string? workflowName, Exception? ex); [LoggerMessage(EventId = 8101, Level = LogLevel.Error, Message = "Error processing DICOMWeb STOW StudyInstanceUID is invalid '{studyInstanceUid}'.")] - public static partial void ErrorDicomWebStowInvalidStudyInstanceUid(this ILogger logger, string studyInstanceUid, Exception ex); + public static partial void ErrorDicomWebStowInvalidStudyInstanceUid(this ILogger logger, string? studyInstanceUid, Exception? ex); [LoggerMessage(EventId = 8102, Level = LogLevel.Warning, Message = "The parameter '{parameterName} with the value of '{parameterValue}' in the multipart message is ignored.")] public static partial void MultipartParameterIgnored(this ILogger logger, string parameterName, string parameterValue); @@ -112,13 +137,13 @@ public static partial class Log public static partial void ConvertingStreamToFileBufferingReadStream(this ILogger logger, int memoryThreashold, string tempPath); [LoggerMessage(EventId = 8104, Level = LogLevel.Error, Message = "Error saving instance from STOW service stream.")] - public static partial void FailedToSaveInstance(this ILogger logger, Exception ex); + public static partial void FailedToSaveInstance(this ILogger logger, Exception? ex); [LoggerMessage(EventId = 8105, Level = LogLevel.Error, Message = "Failed to open STOW service stream.")] - public static partial void FailedToOpenStream(this ILogger logger, Exception ex); + public static partial void FailedToOpenStream(this ILogger logger, Exception? ex); [LoggerMessage(EventId = 8106, Level = LogLevel.Error, Message = "Failed to process STOW request, out of storage space.")] - public static partial void StowFailedWithNoSpace(this ILogger logger, Exception ex = null); + public static partial void StowFailedWithNoSpace(this ILogger logger, Exception? ex = null); [LoggerMessage(EventId = 8108, Level = LogLevel.Information, Message = "STOW instance queued.")] public static partial void QueuedStowInstance(this ILogger logger); @@ -129,6 +154,9 @@ public static partial class Log [LoggerMessage(EventId = 8110, Level = LogLevel.Warning, Message = "Ignoring zero length stream.")] public static partial void ZeroLengthDicomWebStowStream(this ILogger logger); + [LoggerMessage(EventId = 8111, Level = LogLevel.Error, Message = "Unknown virtual application entity specified {aet}.")] + public static partial void ErrorDicomWebStowUnknownVirtualApplicationEntity(this ILogger logger, string? aet, Exception ex); + // FHIR Serer [LoggerMessage(EventId = 8200, Level = LogLevel.Debug, Message = "Parsing FHIR as JSON.")] @@ -145,5 +173,40 @@ public static partial class Log [LoggerMessage(EventId = 8204, Level = LogLevel.Error, Message = "Failed to store FHIR resource.")] public static partial void ErrorStoringFhirResource(this ILogger logger, Exception ex); + + // + // Dicom Associations Controller. + // + [LoggerMessage(EventId = 8300, Level = LogLevel.Error, Message = "Unexpected error occurred in GET /dicom-associations API..")] + public static partial void DicomAssociationsControllerGetError(this ILogger logger, Exception ex); + + /// + /// HL7 Application Configuration controller + /// + [LoggerMessage(EventId = 8400, Level = LogLevel.Error, Message = "Unexpected error occurred in PUT {endpoint} API.")] + public static partial void PutHl7ApplicationConfigException(this ILogger logger, string endpoint, Exception ex); + + + // HL7 Destination Controller + [LoggerMessage(EventId = 8401, Level = LogLevel.Information, Message = "HL7 destination added Name={name}, Host/IP={hostIp}.")] + public static partial void HL7DestinationEntityAdded(this ILogger logger, string name, string hostIp); + + [LoggerMessage(EventId = 8402, Level = LogLevel.Information, Message = "HL7 destination deleted {name}.")] + public static partial void HL7DestinationEntityDeleted(this ILogger logger, string name); + + [LoggerMessage(EventId = 8403, Level = LogLevel.Error, Message = "Error querying HL7 destinations.")] + public static partial void ErrorListingHL7DestinationEntities(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8404, Level = LogLevel.Error, Message = "Error adding new HL7 destination.")] + public static partial void ErrorAddingHL7DestinationEntity(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8405, Level = LogLevel.Error, Message = "Error deleting HL7 destination.")] + public static partial void ErrorDeletingHL7DestinationEntity(this ILogger logger, Exception ex); + + [LoggerMessage(EventId = 8406, Level = LogLevel.Error, Message = "Error C-ECHO to HL7 destination {name}.")] + public static partial void ErrorCEechoHL7DestinationEntity(this ILogger logger, string name, Exception ex); + + [LoggerMessage(EventId = 8407, Level = LogLevel.Information, Message = "HL7 destination updated {name}: Host/IP={hostIp}, Port={port}.")] + public static partial void HL7DestinationEntityUpdated(this ILogger logger, string name, string hostIp, int port); } } diff --git a/src/InformaticsGateway/Logging/LoggingExtensions.cs b/src/InformaticsGateway/Logging/LoggingExtensions.cs deleted file mode 100644 index ca48bc359..000000000 --- a/src/InformaticsGateway/Logging/LoggingExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022 MONAI Consortium - * Copyright 2019-2021 NVIDIA Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Monai.Deploy.InformaticsGateway.Logging -{ - public static class LoggingExtensions - { - public static Microsoft.Extensions.Logging.LogLevel ToMicrosoftExtensionsLogLevel(this FellowOakDicom.Log.LogLevel dicomLogLevel) - { - return dicomLogLevel switch - { - FellowOakDicom.Log.LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, - FellowOakDicom.Log.LogLevel.Fatal => Microsoft.Extensions.Logging.LogLevel.Critical, - FellowOakDicom.Log.LogLevel.Info => Microsoft.Extensions.Logging.LogLevel.Information, - FellowOakDicom.Log.LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, - _ => Microsoft.Extensions.Logging.LogLevel.Debug - }; - } - } -} diff --git a/src/InformaticsGateway/Logging/MicrosoftLoggerAdapter.cs b/src/InformaticsGateway/Logging/MicrosoftLoggerAdapter.cs deleted file mode 100644 index 886bca461..000000000 --- a/src/InformaticsGateway/Logging/MicrosoftLoggerAdapter.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2021-2022 MONAI Consortium - * Copyright 2019-2021 NVIDIA Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using Ardalis.GuardClauses; -using FellowOakDicom.Log; -using Microsoft.Extensions.Logging; - -namespace Monai.Deploy.InformaticsGateway.Logging -{ - /// - /// Implementation of for Microsoft.Extensions.Logging. - /// - public class MicrosoftLoggerAdapter : Logger - { - private readonly Microsoft.Extensions.Logging.ILogger _logger; - - public MicrosoftLoggerAdapter(Microsoft.Extensions.Logging.ILogger logger) => _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - - public override void Log(FellowOakDicom.Log.LogLevel level, string msg, params object[] args) - { - Guard.Against.NullOrWhiteSpace(msg); - - _logger.Log(level.ToMicrosoftExtensionsLogLevel(), msg, args); - } - } -} diff --git a/src/InformaticsGateway/Monai.Deploy.InformaticsGateway.csproj b/src/InformaticsGateway/Monai.Deploy.InformaticsGateway.csproj old mode 100644 new mode 100755 index 3557200d6..fc9f90e3b --- a/src/InformaticsGateway/Monai.Deploy.InformaticsGateway.csproj +++ b/src/InformaticsGateway/Monai.Deploy.InformaticsGateway.csproj @@ -1,5 +1,5 @@ - - - Monai.Deploy.InformaticsGateway Exe - net6.0 + net8.0 Apache-2.0 true True latest ..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset true + be0fffc8-bebb-4509-a2c0-3c981e5415ab + false + enable + true - - - - - - - - - - All - - - - - - - - - - - - - - - + + + + + + + + - <_Parameter1>$(AssemblyName).Test - - - + + + + + + + + - - + - + - + - + - - - + + + + + + \ No newline at end of file diff --git a/src/InformaticsGateway/Program.cs b/src/InformaticsGateway/Program.cs old mode 100644 new mode 100755 index 3f5095a56..42702734b --- a/src/InformaticsGateway/Program.cs +++ b/src/InformaticsGateway/Program.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ using System.IO; using System.IO.Abstractions; using System.Reflection; -using FellowOakDicom.Log; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -26,11 +25,11 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Mllp; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database; -using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Database.EntityFramework.Repositories; using Monai.Deploy.InformaticsGateway.Repositories; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Connectors; @@ -44,10 +43,10 @@ using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.Messaging; using Monai.Deploy.Messaging.Configuration; +using Monai.Deploy.Security.Authentication.Configurations; using Monai.Deploy.Storage; using Monai.Deploy.Storage.Configuration; using NLog; -using NLog.LayoutRenderers; using NLog.Web; using LogManager = NLog.LogManager; @@ -67,6 +66,7 @@ private static void Main(string[] args) logger.Info($"Initializing MONAI Deploy Informatics Gateway v{assemblyVersionNumber}"); var host = CreateHostBuilder(args).Build(); + host.MigrateDatabase(); host.Run(); logger.Info("MONAI Deploy Informatics Gateway shutting down."); @@ -93,18 +93,17 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => builder.ClearProviders(); builder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); }) + .UseNLog() .ConfigureServices((hostContext, services) => { - services.AddOptions() - .Bind(hostContext.Configuration.GetSection("InformaticsGateway")); - services.AddOptions() - .Bind(hostContext.Configuration.GetSection("InformaticsGateway:messaging")); - services.AddOptions() - .Bind(hostContext.Configuration.GetSection("InformaticsGateway:storage")); - + services.AddOptions().Bind(hostContext.Configuration.GetSection("InformaticsGateway")); + services.AddOptions().Bind(hostContext.Configuration.GetSection("InformaticsGateway:httpEndpointSettings")); + services.AddOptions().Bind(hostContext.Configuration.GetSection("InformaticsGateway:messaging")); + services.AddOptions().Bind(hostContext.Configuration.GetSection("InformaticsGateway:storage")); + services.AddOptions().Bind(hostContext.Configuration.GetSection("MonaiDeployAuthentication")); + services.AddOptions().Bind(hostContext.Configuration.GetSection("plugins")); services.TryAddEnumerable(ServiceDescriptor.Singleton, ConfigurationValidator>()); - - services.ConfigureDatabase(hostContext.Configuration?.GetSection("ConnectionStrings")); + services.ConfigureDatabase(hostContext.Configuration?.GetSection("ConnectionStrings"), hostContext.Configuration?.GetSection("plugins"), services.BuildServiceProvider().GetService()!); services.AddTransient(); services.AddTransient(); @@ -112,35 +111,33 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, InputDataPlugInEngineFactory>(); + services.AddScoped, OutputDataPlugInEngineFactory>(); + services.AddScoped, InputHL7DataPlugInEngineFactory>(); - services.AddMonaiDeployStorageService(hostContext.Configuration.GetSection("InformaticsGateway:storage:serviceAssemblyName").Value, Monai.Deploy.Storage.HealthCheckOptions.ServiceHealthCheck); + services.AddMonaiDeployStorageService(hostContext.Configuration!.GetSection("InformaticsGateway:storage:serviceAssemblyName").Value!, Monai.Deploy.Storage.HealthCheckOptions.ServiceHealthCheck); - services.AddMonaiDeployMessageBrokerPublisherService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:publisherServiceAssemblyName").Value, true); - services.AddMonaiDeployMessageBrokerSubscriberService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:subscriberServiceAssemblyName").Value, true); + services.AddMonaiDeployMessageBrokerPublisherService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:publisherServiceAssemblyName").Value!, true); + services.AddMonaiDeployMessageBrokerSubscriberService(hostContext.Configuration.GetSection("InformaticsGateway:messaging:subscriberServiceAssemblyName").Value!, true); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); var timeout = TimeSpan.FromSeconds(hostContext.Configuration.GetValue("InformaticsGateway:dicomWeb:clientTimeout", DicomWebConfiguration.DefaultClientTimeout)); services @@ -157,14 +154,18 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => .AddHttpClient("fhir", configure => configure.Timeout = timeout) .SetHandlerLifetime(timeout); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); - services.AddHostedService(p => p.GetService()); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + services.AddHostedService(); + }) .ConfigureWebHostDefaults(webBuilder => { @@ -172,15 +173,19 @@ internal static IHostBuilder CreateHostBuilder(string[] args) => webBuilder.CaptureStartupErrors(true); webBuilder.UseStartup(); }) - .UseNLog(); + ; private static NLog.Logger ConfigureNLog(string assemblyVersionNumber) { - LayoutRenderer.Register("servicename", logEvent => typeof(Program).Namespace); - LayoutRenderer.Register("serviceversion", logEvent => assemblyVersionNumber); - LayoutRenderer.Register("machinename", logEvent => Environment.MachineName); - - return LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); + return LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayoutRenderer("servicename", logEvent => typeof(Program).Namespace); + ext.RegisterLayoutRenderer("serviceversion", logEvent => assemblyVersionNumber); + ext.RegisterLayoutRenderer("machinename", logEvent => Environment.MachineName); + ext.RegisterLayoutRenderer("appname", logEvent => "MIG"); + }) + .LoadConfigurationFromAppSettings() + .GetCurrentClassLogger(); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Properties/launchSettings.json b/src/InformaticsGateway/Properties/launchSettings.json deleted file mode 100644 index 9218385b9..000000000 --- a/src/InformaticsGateway/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "Monai.Deploy.InformaticsGateway": { - "commandName": "Project", - "environmentVariables": { - "DOTNET_ENVIRONMENT": "Development", - "ELASTIC_CLIENT_APIVERSIONING": "true", - "LOGSTASH_URL": "tcp://localhost:50000" - } - } - } -} \ No newline at end of file diff --git a/src/InformaticsGateway/Repositories/MonaiServiceLocator.cs b/src/InformaticsGateway/Repositories/MonaiServiceLocator.cs old mode 100644 new mode 100755 index e94c9eeea..3fd78bfc2 --- a/src/InformaticsGateway/Repositories/MonaiServiceLocator.cs +++ b/src/InformaticsGateway/Repositories/MonaiServiceLocator.cs @@ -19,6 +19,8 @@ using System.Linq; using System.Reflection; using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Services.Common; @@ -47,11 +49,18 @@ public Dictionary GetServiceStatus() return _runningServices.ToDictionary(k => k.ServiceName, v => v.Status); } - private IMonaiService GetService(Type type) + private IMonaiService? GetService(Type type) { - Guard.Against.Null(type); + Guard.Against.Null(type, nameof(type)); + + var TypeInterface = type.GetInterfaces().FirstOrDefault(i => i.Name == $"I{type.Name}"); + if (TypeInterface is null) + { + var service = _serviceProvider.GetServices()?.FirstOrDefault(i => i.GetType().Name == type.Name); + return service as IMonaiService; + } + return (_serviceProvider.GetService(TypeInterface) as IMonaiService); - return _serviceProvider.GetService(type) as IMonaiService; } private static List LocateTypes() @@ -62,11 +71,11 @@ private static List LocateTypes() serviceType.IsAssignableFrom(p) && p != serviceType && !p.IsAbstract && - p.FullName.StartsWith("Monai", StringComparison.InvariantCulture)); + p.FullName!.StartsWith("Monai", StringComparison.InvariantCulture)); return services.Distinct().ToList(); } - private IList LocateServices() + private List LocateServices() { var list = new List(); foreach (var t in _types) diff --git a/src/InformaticsGateway/Services/Common/AuthenticationHeaderValueExtensions.cs b/src/InformaticsGateway/Services/Common/AuthenticationHeaderValueExtensions.cs old mode 100644 new mode 100755 index fe5d3a2bc..4a623a47f --- a/src/InformaticsGateway/Services/Common/AuthenticationHeaderValueExtensions.cs +++ b/src/InformaticsGateway/Services/Common/AuthenticationHeaderValueExtensions.cs @@ -22,7 +22,7 @@ namespace Monai.Deploy.InformaticsGateway.Services.Common { public static class AuthenticationHeaderValueExtensions { - public static AuthenticationHeaderValue ConvertFrom(ConnectionAuthType connectionAuthType, string authId) + public static AuthenticationHeaderValue? ConvertFrom(ConnectionAuthType connectionAuthType, string authId) { return connectionAuthType switch { diff --git a/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs b/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs new file mode 100755 index 000000000..880eaef95 --- /dev/null +++ b/src/InformaticsGateway/Services/Common/IInputDataPluginEngineFactory.cs @@ -0,0 +1,135 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using Ardalis.GuardClauses; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + public interface IDataPlugInEngineFactory + { + IReadOnlyDictionary RegisteredPlugIns(); + } + + public static class DataPlugInEngineFactoryStatic + { + public static readonly object SyncLock = new(); + } + + public abstract class DataPlugInEngineFactoryBase : IDataPlugInEngineFactory + { + private static readonly object SyncLock = new(); + private readonly IFileSystem _fileSystem; + private readonly ILogger> _logger; + private readonly Type _type; + + /// + /// A dictionary mapping of input data plug-ins where: + /// key: if available or name of the class. + /// value: fully qualified assembly type + /// + private readonly Dictionary _cachedTypeNames; + + protected DataPlugInEngineFactoryBase(IFileSystem fileSystem, ILogger> logger) + { + _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); + _logger = logger ?? throw new System.ArgumentNullException(nameof(logger)); + _type = typeof(T); + _cachedTypeNames = new Dictionary(); + } + + public IReadOnlyDictionary RegisteredPlugIns() + { + LoadAssembliesFromPlugInsDirectory(); + + var types = AppDomain.CurrentDomain.GetAssemblies() + .Where(p => !p.FullName!.Contains("DynamicProxyGenAssembly2")) + .SelectMany(s => s.GetTypes()) + .Where(p => _type.IsAssignableFrom(p) && p != _type).ToList(); + + AddToCache(types); + + return _cachedTypeNames; + } + + private void AddToCache(List types) + { + Guard.Against.Null(types, nameof(types)); + + if (types.Any()) + { + types.ForEach(p => + { + if (!_cachedTypeNames.ContainsValue(p.GetShortTypeAssemblyName())) + { + var nameAttribute = p.GetCustomAttribute(); + + var name = nameAttribute is null ? p.Name : nameAttribute.Name; + _cachedTypeNames.Add(name, p.GetShortTypeAssemblyName()); + _logger.DataPlugInFound(_type.Name, name, p.GetShortTypeAssemblyName()); + } + }); + } + } + + private void LoadAssembliesFromPlugInsDirectory() + { + lock (DataPlugInEngineFactoryStatic.SyncLock) + { + var files = _fileSystem.Directory.GetFiles(SR.PlugInDirectoryPath, "*.dll", System.IO.SearchOption.TopDirectoryOnly); + + foreach (var file in files) + { + _logger.LoadingAssembly(file); + var assembly = Assembly.Load(File.ReadAllBytes(file)); + var matchingTypes = assembly.GetTypes().Where(p => _type.IsAssignableFrom(p) && p != _type).ToList(); + + AddToCache(matchingTypes); + } + } + } + } + + public class InputDataPlugInEngineFactory : DataPlugInEngineFactoryBase + { + public InputDataPlugInEngineFactory(IFileSystem fileSystem, ILogger> logger) : base(fileSystem, logger) + { + } + } + + public class OutputDataPlugInEngineFactory : DataPlugInEngineFactoryBase + { + public OutputDataPlugInEngineFactory(IFileSystem fileSystem, ILogger> logger) : base(fileSystem, logger) + { + } + } + + public class InputHL7DataPlugInEngineFactory : DataPlugInEngineFactoryBase + { + public InputHL7DataPlugInEngineFactory(IFileSystem fileSystem, ILogger> logger) : base(fileSystem, logger) + { + } + } +} diff --git a/src/InformaticsGateway/Services/Common/ITcpListener.cs b/src/InformaticsGateway/Services/Common/ITcpListener.cs index 83fac6edb..d3e37bda3 100644 --- a/src/InformaticsGateway/Services/Common/ITcpListener.cs +++ b/src/InformaticsGateway/Services/Common/ITcpListener.cs @@ -36,7 +36,7 @@ internal class TcpListener : ITcpListener public TcpListener(IPAddress ipAddress, int port) { - Guard.Against.Null(ipAddress); + Guard.Against.Null(ipAddress, nameof(ipAddress)); _tcpListener = new System.Net.Sockets.TcpListener(ipAddress, port); } diff --git a/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs b/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs new file mode 100755 index 000000000..cbc93711c --- /dev/null +++ b/src/InformaticsGateway/Services/Common/InputDataPluginEngine.cs @@ -0,0 +1,84 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using FellowOakDicom; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + internal class InputDataPlugInEngine(IServiceProvider serviceProvider, ILogger logger) : IInputDataPlugInEngine + { + private readonly IServiceProvider _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + private IReadOnlyList? _plugins; + + public void Configure(IReadOnlyList pluginAssemblies) + { + _plugins = LoadPlugIns(_serviceProvider, pluginAssemblies); + } + + public async Task> ExecutePlugInsAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + if (_plugins == null) + { + throw new PlugInInitializationException("InputDataPlugInEngine not configured, please call Configure() first."); + } + + foreach (var plugin in _plugins) + { + _logger.ExecutingInputDataPlugIn(plugin.Name); + (dicomFile, fileMetadata) = await plugin.ExecuteAsync(dicomFile, fileMetadata).ConfigureAwait(false); + + _logger.ExecutedInputDataPlugIn(plugin.Name, JsonSerializer.Serialize(fileMetadata)); + } + + return new Tuple(dicomFile, fileMetadata); + } + + private List LoadPlugIns(IServiceProvider serviceProvider, IReadOnlyList pluginAssemblies) + { + var exceptions = new List(); + var list = new List(); + foreach (var plugin in pluginAssemblies) + { + try + { + _logger.AddingInputDataPlugIn(plugin); + list.Add(typeof(IInputDataPlugIn).CreateInstance(serviceProvider, typeString: plugin)); + } + catch (Exception ex) + { + exceptions.Add(new PlugInLoadingException($"Error loading plug-in '{plugin}'.", ex)); + } + } + + if (exceptions.Count is not 0) + { + throw new AggregateException("Error loading plug-in(s).", exceptions); + } + + return list; + } + } +} diff --git a/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs b/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs new file mode 100755 index 000000000..afd858c9d --- /dev/null +++ b/src/InformaticsGateway/Services/Common/InputHL7DataPlugInEngine.cs @@ -0,0 +1,87 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + public class InputHL7DataPlugInEngine(IServiceProvider serviceProvider, ILogger logger) : IInputHL7DataPlugInEngine + { + private readonly IServiceProvider _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + private IReadOnlyList? _plugsins; + + public void Configure(IReadOnlyList pluginAssemblies) + { + _plugsins = LoadPlugIns(_serviceProvider, pluginAssemblies); + } + + public async Task> ExecutePlugInsAsync(Message hl7File, FileStorageMetadata fileMetadata, Hl7ApplicationConfigEntity? configItem) + { + if (configItem?.PlugInAssemblies is not null && configItem.PlugInAssemblies.Count is not 0) + { + if (_plugsins == null) + { + throw new PlugInInitializationException("InputHL7DataPlugInEngine not configured, please call Configure() first."); + } + + foreach (var plugin in _plugsins) + { + if (configItem.PlugInAssemblies.Exists(a => a.StartsWith(plugin.ToString()!))) + { + _logger.ExecutingInputDataPlugIn(plugin.Name); + (hl7File, fileMetadata) = await plugin.ExecuteAsync(hl7File, fileMetadata).ConfigureAwait(false); + } + } + } + return new Tuple(hl7File, fileMetadata); + } + + private List LoadPlugIns(IServiceProvider serviceProvider, IReadOnlyList pluginAssemblies) + { + var exceptions = new List(); + var list = new List(); + foreach (var plugin in pluginAssemblies) + { + try + { + _logger.AddingInputDataPlugIn(plugin); + list.Add(typeof(IInputHL7DataPlugIn).CreateInstance(serviceProvider, typeString: plugin)); + } + catch (Exception ex) + { + exceptions.Add(new PlugInLoadingException($"Error loading plug-in '{plugin}'.", ex)); + } + } + + if (exceptions.Count is not 0) + { + throw new AggregateException("Error loading plug-in(s).", exceptions); + } + + return list; + } + } +} diff --git a/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs b/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs new file mode 100755 index 000000000..a497862dc --- /dev/null +++ b/src/InformaticsGateway/Services/Common/OutputDataPluginEngine.cs @@ -0,0 +1,96 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + internal class OutputDataPlugInEngine : IOutputDataPlugInEngine + { + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly IDicomToolkit _dicomToolkit; + private IReadOnlyList? _plugsins; + + public OutputDataPlugInEngine(IServiceProvider serviceProvider, ILogger logger, IDicomToolkit dicomToolkit) + { + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); + } + + public void Configure(IReadOnlyList pluginAssemblies) + { + _plugsins = LoadPlugIns(_serviceProvider, pluginAssemblies); + } + + public async Task ExecutePlugInsAsync(ExportRequestDataMessage exportRequestDataMessage) + { + if (_plugsins == null) + { + throw new PlugInInitializationException("InputDataPlugInEngine not configured, please call Configure() first."); + } + + var dicomFile = _dicomToolkit.Load(exportRequestDataMessage.FileContent); + foreach (var plugin in _plugsins) + { + _logger.ExecutingOutputDataPlugIn(plugin.Name); + (dicomFile, exportRequestDataMessage) = await plugin.ExecuteAsync(dicomFile, exportRequestDataMessage).ConfigureAwait(false); + } + using var ms = new MemoryStream(); + await dicomFile.SaveAsync(ms).ConfigureAwait(false); + exportRequestDataMessage.SetData(ms.ToArray()); + + return exportRequestDataMessage; + } + + private IReadOnlyList LoadPlugIns(IServiceProvider serviceProvider, IReadOnlyList pluginAssemblies) + { + var exceptions = new List(); + var list = new List(); + foreach (var plugin in pluginAssemblies) + { + try + { + _logger.AddingOutputDataPlugIn(plugin); + list.Add(typeof(IOutputDataPlugIn).CreateInstance(serviceProvider, typeString: plugin)); + } + catch (Exception ex) + { + _logger.ErrorAddingOutputDataPlugIn(ex, plugin); + exceptions.Add(new PlugInLoadingException($"Error loading plug-in '{plugin}'.", ex)); + } + } + + if (exceptions.Any()) + { + throw new AggregateException("Error loading plug-in(s).", exceptions); + } + + return list; + } + } +} diff --git a/src/InformaticsGateway/Services/Common/Pagination/PagedResponse.cs b/src/InformaticsGateway/Services/Common/Pagination/PagedResponse.cs new file mode 100644 index 000000000..00fa1d355 --- /dev/null +++ b/src/InformaticsGateway/Services/Common/Pagination/PagedResponse.cs @@ -0,0 +1,107 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using Monai.Deploy.InformaticsGateway.Services.UriService; + +namespace Monai.Deploy.InformaticsGateway.Services.Common.Pagination +{ + /// + /// Paged Response for use with pagination's. + /// + /// Type of response. + public class PagedResponse : Response + { + /// + /// Initializes a new instance of the class. + /// + /// Response Data. + /// Page number. + /// Page size. + public PagedResponse(T data, int pageNumber, int pageSize) + { + PageNumber = pageNumber; + PageSize = pageSize; + Data = data; + Message = null; + Succeeded = true; + Errors = null; + } + + /// + /// Gets or sets PageNumber. + /// + public int PageNumber { get; set; } + + /// + /// Gets or sets PageSize. + /// + public int PageSize { get; set; } + + /// + /// Gets or sets FirstPage. + /// + public string? FirstPage { get; set; } + + /// + /// Gets or sets LastPage. + /// + public string? LastPage { get; set; } + + /// + /// Gets or sets TotalPages. + /// + public int TotalPages { get; set; } + + /// + /// Gets or sets TotalRecords. + /// + public long TotalRecords { get; set; } + + /// + /// Gets or sets NextPage. + /// + public string? NextPage { get; set; } + + /// + /// Gets or sets previousPage. + /// + public string? PreviousPage { get; set; } + + public void SetUp(PaginationFilter validFilter, long totalRecords, IUriService uriService, string route) + { + var totalPages = (double)totalRecords / PageSize; + var roundedTotalPages = Convert.ToInt32(Math.Ceiling(totalPages)); + + var pageNumber = validFilter.PageNumber ?? 0; + NextPage = + pageNumber >= 1 && pageNumber < roundedTotalPages + ? uriService.GetPageUriString(new PaginationFilter(pageNumber + 1, PageSize), route) + : null; + + PreviousPage = + pageNumber - 1 >= 1 && pageNumber <= roundedTotalPages + ? uriService.GetPageUriString(new PaginationFilter(pageNumber - 1, PageSize), route) + : null; + + FirstPage = uriService.GetPageUriString(new PaginationFilter(1, PageSize), route); + LastPage = uriService.GetPageUriString(new PaginationFilter(roundedTotalPages, PageSize), route); + TotalPages = roundedTotalPages; + TotalRecords = totalRecords; + } + } +} diff --git a/src/InformaticsGateway/Services/Common/Pagination/PaginationFilter.cs b/src/InformaticsGateway/Services/Common/Pagination/PaginationFilter.cs new file mode 100644 index 000000000..9183c2005 --- /dev/null +++ b/src/InformaticsGateway/Services/Common/Pagination/PaginationFilter.cs @@ -0,0 +1,58 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Monai.Deploy.InformaticsGateway.Services.Common.Pagination +{ + /// + /// Pagination Filter class. + /// + public class PaginationFilter + { + /// + /// Initializes a new instance of the class. + /// + public PaginationFilter() + { + PageNumber = 1; + PageSize = null; + } + + /// + /// Initializes a new instance of the class. + /// + /// Page size with limit set in the config. + /// Page size 1 or above. + /// Max page size. + public PaginationFilter(int pageNumber, int pageSize, int maxPageSize = 10) + { + PageNumber = pageNumber < 1 ? 1 : pageNumber; + PageSize = pageSize > maxPageSize ? maxPageSize : pageSize; + } + + /// + /// Gets or sets page number. + /// + public int? PageNumber { get; set; } + + /// + /// Gets or sets page size. + /// + public int? PageSize { get; set; } + + public int GetSkip() => (PageNumber - 1) * PageSize ?? 0; + } +} diff --git a/src/InformaticsGateway/Services/Common/Pagination/Response.cs b/src/InformaticsGateway/Services/Common/Pagination/Response.cs new file mode 100644 index 000000000..ba0bec7b9 --- /dev/null +++ b/src/InformaticsGateway/Services/Common/Pagination/Response.cs @@ -0,0 +1,67 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Services.Common.Pagination +{ + /// + /// Response object. + /// + /// Type of response data. + public class Response + { + /// + /// Initializes a new instance of the class. + /// + public Response() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Response data. + public Response(T data) + { + Succeeded = true; + Message = string.Empty; + Errors = Array.Empty(); + Data = data; + } + + /// + /// Gets or sets Data. + /// + public T? Data { get; set; } + + /// + /// Gets or sets a value indicating whether response has succeeded. + /// + public bool Succeeded { get; set; } + + /// + /// Gets or sets errors. + /// + public string[]? Errors { get; set; } = Array.Empty(); + + /// + /// Gets or sets message. + /// + public string? Message { get; set; } + } +} diff --git a/src/InformaticsGateway/Services/Common/Pagination/TimeFilter.cs b/src/InformaticsGateway/Services/Common/Pagination/TimeFilter.cs new file mode 100644 index 000000000..20f09e962 --- /dev/null +++ b/src/InformaticsGateway/Services/Common/Pagination/TimeFilter.cs @@ -0,0 +1,51 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Services.Common.Pagination +{ + public class TimeFilter : PaginationFilter + { + public TimeFilter() + { + } + + public TimeFilter(DateTime? startTime, + DateTime? endTime, + int pageNumber, + int pageSize, + int maxPageSize) : base(pageNumber, + pageSize, + maxPageSize) + { + if (endTime == default) + { + EndTime = DateTime.Now; + } + + if (startTime == default) + { + StartTime = new DateTime(2023, 1, 1); + } + } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + } +} diff --git a/src/InformaticsGateway/Services/Common/ScpInputTypeEnum.cs b/src/InformaticsGateway/Services/Common/ScpInputTypeEnum.cs new file mode 100755 index 000000000..280effdfe --- /dev/null +++ b/src/InformaticsGateway/Services/Common/ScpInputTypeEnum.cs @@ -0,0 +1,24 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Monai.Deploy.InformaticsGateway.Services.Common +{ + public enum ScpInputTypeEnum + { + WorkflowTrigger, + ExternalAppReturn + } +} diff --git a/src/InformaticsGateway/Services/Common/TcpClientAdapter.cs b/src/InformaticsGateway/Services/Common/TcpClientAdapter.cs old mode 100644 new mode 100755 index 8ecfead8e..460608554 --- a/src/InformaticsGateway/Services/Common/TcpClientAdapter.cs +++ b/src/InformaticsGateway/Services/Common/TcpClientAdapter.cs @@ -28,7 +28,7 @@ public EndPoint RemoteEndPoint { get { - return _tcpClient.Client.RemoteEndPoint; + return _tcpClient.Client!.RemoteEndPoint!; } } diff --git a/src/InformaticsGateway/Services/Connectors/DataRetrievalService.cs b/src/InformaticsGateway/Services/Connectors/DataRetrievalService.cs old mode 100644 new mode 100755 index 88cf26757..821167ce9 --- a/src/InformaticsGateway/Services/Connectors/DataRetrievalService.cs +++ b/src/InformaticsGateway/Services/Connectors/DataRetrievalService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; @@ -40,6 +39,7 @@ using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Events; using Polly; namespace Monai.Deploy.InformaticsGateway.Services.Connectors @@ -119,11 +119,11 @@ private async Task BackgroundProcessing(CancellationToken cancellationToken) continue; } - InferenceRequest request = null; + InferenceRequest? request = null; try { request = await repository.TakeAsync(cancellationToken).ConfigureAwait(false); - using (_logger.BeginScope(new LoggingDataDictionary { { "TransactionId", request.TransactionId } })) + using (_logger.BeginScope(new Api.LoggingDataDictionary { { "TransactionId", request.TransactionId } })) { _logger.ProcessingInferenceRequest(); await ProcessRequest(request, cancellationToken).ConfigureAwait(false); @@ -141,7 +141,7 @@ private async Task BackgroundProcessing(CancellationToken cancellationToken) } catch (Exception ex) { - _logger.ErrorProcessingInferenceRequest(request?.TransactionId, ex); + _logger.ErrorProcessingInferenceRequest(request?.TransactionId!, ex); if (request != null) { await repository.UpdateAsync(request, InferenceRequestStatus.Fail, cancellationToken).ConfigureAwait(false); @@ -154,14 +154,14 @@ private async Task BackgroundProcessing(CancellationToken cancellationToken) private async Task ProcessRequest(InferenceRequest inferenceRequest, CancellationToken cancellationToken) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); var retrievedFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); await RestoreExistingInstances(inferenceRequest, retrievedFiles, cancellationToken).ConfigureAwait(false); - foreach (var source in inferenceRequest.InputResources) + foreach (var source in inferenceRequest.InputResources!) { - _logger.ProcessingInputResource(source.Interface, source.ConnectionDetails.Uri); + _logger.ProcessingInputResource(source.Interface, source.ConnectionDetails!.Uri!); switch (source.Interface) { case InputInterfaceType.DicomWeb: @@ -185,7 +185,7 @@ private async Task ProcessRequest(InferenceRequest inferenceRequest, Cancellatio private async Task NotifyNewInstance(InferenceRequest inferenceRequest, Dictionary retrievedFiles) { - Guard.Against.Null(inferenceRequest); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); if (retrievedFiles.IsNullOrEmpty()) { @@ -196,17 +196,19 @@ private async Task NotifyNewInstance(InferenceRequest inferenceRequest, Dictiona { if (inferenceRequest.Application is not null) { - retrievedFiles[key].SetWorkflows(inferenceRequest.Application.Id); + retrievedFiles[key].SetWorkflows(inferenceRequest.Application.Id!); } + + var payloadId = await _payloadAssembler.Queue(inferenceRequest.TransactionId, retrievedFiles[key], new DataOrigin { DataService = DataService.ACR, Source = inferenceRequest.TransactionId, Destination = FileStorageMetadata.IpAddress() }).ConfigureAwait(false); + retrievedFiles[key].PayloadId = payloadId.ToString(); _uploadQueue.Queue(retrievedFiles[key]); - await _payloadAssembler.Queue(inferenceRequest.TransactionId, retrievedFiles[key]).ConfigureAwait(false); } } private async Task RestoreExistingInstances(InferenceRequest inferenceRequest, Dictionary retrievedInstances, CancellationToken cancellationToken) { - Guard.Against.Null(inferenceRequest); - Guard.Against.Null(retrievedInstances); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); + Guard.Against.Null(retrievedInstances, nameof(retrievedInstances)); using var scope = _serviceScopeFactory.CreateScope(); @@ -236,10 +238,10 @@ private async Task RestoreExistingInstances(InferenceRequest inferenceRequest, D private async Task RetrieveViaFhir(InferenceRequest inferenceRequest, RequestInputDataResource source, Dictionary retrievedResources, CancellationToken cancellationToken) { - Guard.Against.Null(inferenceRequest); - Guard.Against.Null(retrievedResources); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); + Guard.Against.Null(retrievedResources, nameof(retrievedResources)); - foreach (var input in inferenceRequest.InputMetadata.Inputs) + foreach (var input in inferenceRequest.InputMetadata!.Inputs!) { if (cancellationToken.IsCancellationRequested) { @@ -255,25 +257,25 @@ private async Task RetrieveViaFhir(InferenceRequest inferenceRequest, RequestInp private async Task RetrieveFhirResources(string transactionId, InferenceRequestDetails requestDetails, RequestInputDataResource source, Dictionary retrievedResources, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(requestDetails); - Guard.Against.Null(source); - Guard.Against.Null(retrievedResources); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(requestDetails, nameof(requestDetails)); + Guard.Against.Null(source, nameof(source)); + Guard.Against.Null(retrievedResources, nameof(retrievedResources)); - var pendingResources = new Queue(requestDetails.Resources.Where(p => !p.IsRetrieved)); + var pendingResources = new Queue(requestDetails.Resources!.Where(p => !p.IsRetrieved)); if (pendingResources.Count == 0) { return; } - var authenticationHeaderValue = AuthenticationHeaderValueExtensions.ConvertFrom(source.ConnectionDetails.AuthType, source.ConnectionDetails.AuthId); + var authenticationHeaderValue = AuthenticationHeaderValueExtensions.ConvertFrom(source.ConnectionDetails!.AuthType!, source.ConnectionDetails!.AuthId!); var httpClient = _httpClientFactory.CreateClient("fhir"); httpClient.DefaultRequestHeaders.Clear(); httpClient.DefaultRequestHeaders.Authorization = authenticationHeaderValue; - FhirResource resource = null; + FhirResource? resource = null; try { while (pendingResources.Count > 0) @@ -304,12 +306,12 @@ private async Task RetrieveFhirResources(string transactionId, InferenceRequestD private async Task RetrieveFhirResource(string transactionId, HttpClient httpClient, FhirResource resource, RequestInputDataResource source, Dictionary retrievedResources, FhirStorageFormat fhirFormat, string acceptHeader, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(httpClient); - Guard.Against.Null(resource); - Guard.Against.Null(source); - Guard.Against.Null(retrievedResources); - Guard.Against.NullOrWhiteSpace(acceptHeader); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(httpClient, nameof(httpClient)); + Guard.Against.Null(resource, nameof(resource)); + Guard.Against.Null(source, nameof(source)); + Guard.Against.Null(retrievedResources, nameof(retrievedResources)); + Guard.Against.NullOrWhiteSpace(acceptHeader, nameof(acceptHeader)); var id = $"{resource.Type}/{resource.Id}"; if (retrievedResources.ContainsKey(id)) @@ -319,7 +321,7 @@ private async Task RetrieveFhirResource(string transactionId, HttpClient h } _logger.RetrievingFhirResource(resource.Type, resource.Id, acceptHeader, fhirFormat); - var request = new HttpRequestMessage(HttpMethod.Get, $"{source.ConnectionDetails.Uri}{resource.Type}/{resource.Id}"); + var request = new HttpRequestMessage(HttpMethod.Get, $"{source.ConnectionDetails!.Uri!}{resource.Type}/{resource.Id}"); request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(acceptHeader)); var response = await Policy .HandleResult(p => !p.IsSuccessStatusCode) @@ -327,7 +329,7 @@ private async Task RetrieveFhirResource(string transactionId, HttpClient h _options.Value.Fhir.Retries.RetryDelays, (result, timeSpan, retryCount, context) => { - _logger.ErrorRetrievingFhirResourceWithRetry(resource.Type, resource.Id, result.Result.StatusCode, retryCount, result.Exception); + _logger.ErrorRetrievingFhirResourceWithRetry(resource.Type, resource.Id, result.Result!.StatusCode!, retryCount, result.Exception); }) .ExecuteAsync(async () => await httpClient.SendAsync(request).ConfigureAwait(false)).ConfigureAwait(false); @@ -341,7 +343,7 @@ private async Task RetrieveFhirResource(string transactionId, HttpClient h return false; } - var fhirFile = new FhirFileStorageMetadata(transactionId, resource.Type, resource.Id, fhirFormat); + var fhirFile = new FhirFileStorageMetadata(transactionId, resource.Type, resource.Id, fhirFormat, DataService.FHIR, source.ConnectionDetails!.Uri!); await fhirFile.SetDataStream(json, _options.Value.Storage.TemporaryDataStorage, _fileSystem, _options.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); retrievedResources.Add(fhirFile.Id, fhirFile); return true; @@ -355,20 +357,20 @@ private async Task RetrieveFhirResource(string transactionId, HttpClient h private async Task RetrieveViaDicomWeb(InferenceRequest inferenceRequest, RequestInputDataResource source, Dictionary retrievedInstance, CancellationToken cancellationToken) { - Guard.Against.Null(inferenceRequest); - Guard.Against.Null(retrievedInstance); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); - var authenticationHeaderValue = AuthenticationHeaderValueExtensions.ConvertFrom(source.ConnectionDetails.AuthType, source.ConnectionDetails.AuthId); + var authenticationHeaderValue = AuthenticationHeaderValueExtensions.ConvertFrom(source.ConnectionDetails!.AuthType, source.ConnectionDetails.AuthId!); var dicomWebClient = new DicomWebClient(_httpClientFactory.CreateClient("dicomweb"), _loggerFactory.CreateLogger()); - dicomWebClient.ConfigureServiceUris(new Uri(source.ConnectionDetails.Uri, UriKind.Absolute)); + dicomWebClient.ConfigureServiceUris(new Uri(source.ConnectionDetails.Uri!, UriKind.Absolute)); if (authenticationHeaderValue is not null) { dicomWebClient.ConfigureAuthentication(authenticationHeaderValue); } - foreach (var input in inferenceRequest.InputMetadata.Inputs) + foreach (var input in inferenceRequest.InputMetadata!.Inputs!) { if (cancellationToken.IsCancellationRequested) { @@ -378,15 +380,15 @@ private async Task RetrieveViaDicomWeb(InferenceRequest inferenceRequest, Reques switch (input.Type) { case InferenceRequestType.DicomUid: - await RetrieveStudies(inferenceRequest.TransactionId, dicomWebClient, input.Studies, retrievedInstance, cancellationToken).ConfigureAwait(false); + await RetrieveStudies(inferenceRequest.TransactionId, dicomWebClient, input.Studies!, retrievedInstance, cancellationToken).ConfigureAwait(false); break; case InferenceRequestType.DicomPatientId: - await QueryStudies(inferenceRequest.TransactionId, dicomWebClient, inferenceRequest, retrievedInstance, $"{DicomTag.PatientID.Group:X4}{DicomTag.PatientID.Element:X4}", input.PatientId, cancellationToken).ConfigureAwait(false); + await QueryStudies(inferenceRequest.TransactionId, dicomWebClient, inferenceRequest, retrievedInstance, $"{DicomTag.PatientID.Group:X4}{DicomTag.PatientID.Element:X4}", input.PatientId!, cancellationToken).ConfigureAwait(false); break; case InferenceRequestType.AccessionNumber: - foreach (var accessionNumber in input.AccessionNumber) + foreach (var accessionNumber in input.AccessionNumber!) { await QueryStudies(inferenceRequest.TransactionId, dicomWebClient, inferenceRequest, retrievedInstance, $"{DicomTag.AccessionNumber.Group:X4}{DicomTag.AccessionNumber.Element:X4}", accessionNumber, cancellationToken).ConfigureAwait(false); } @@ -402,12 +404,12 @@ private async Task RetrieveViaDicomWeb(InferenceRequest inferenceRequest, Reques private async Task QueryStudies(string transactionId, DicomWebClient dicomWebClient, InferenceRequest inferenceRequest, Dictionary retrievedInstance, string dicomTag, string queryValue, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(dicomWebClient); - Guard.Against.Null(inferenceRequest); - Guard.Against.Null(retrievedInstance); - Guard.Against.NullOrWhiteSpace(dicomTag); - Guard.Against.NullOrWhiteSpace(queryValue); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(dicomWebClient, nameof(dicomWebClient)); + Guard.Against.Null(inferenceRequest, nameof(inferenceRequest)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); + Guard.Against.NullOrWhiteSpace(dicomTag, nameof(dicomTag)); + Guard.Against.NullOrWhiteSpace(queryValue, nameof(queryValue)); _logger.PerformQido(dicomTag, queryValue); var queryParams = new Dictionary(StringComparer.OrdinalIgnoreCase) @@ -445,9 +447,9 @@ private async Task QueryStudies(string transactionId, DicomWebClient dicomWebCli private async Task RetrieveStudies(string transactionId, IDicomWebClient dicomWebClient, IList studies, Dictionary retrievedInstance, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(studies); - Guard.Against.Null(retrievedInstance); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(studies, nameof(studies)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); foreach (var study in studies) { @@ -457,7 +459,7 @@ private async Task RetrieveStudies(string transactionId, IDicomWebClient dicomWe } if (study.Series.IsNullOrEmpty()) { - _logger.RetrievingStudyWithWado(study.StudyInstanceUid); + _logger.RetrievingStudyWithWado(study.StudyInstanceUid!); var files = dicomWebClient.Wado.Retrieve(study.StudyInstanceUid); await SaveFiles(transactionId, files, retrievedInstance, cancellationToken).ConfigureAwait(false); } @@ -470,11 +472,11 @@ private async Task RetrieveStudies(string transactionId, IDicomWebClient dicomWe private async Task RetrieveSeries(string transactionId, IDicomWebClient dicomWebClient, RequestedStudy study, Dictionary retrievedInstance, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(study); - Guard.Against.Null(retrievedInstance); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(study, nameof(study)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); - foreach (var series in study.Series) + foreach (var series in study.Series!) { if (cancellationToken.IsCancellationRequested) { @@ -482,28 +484,28 @@ private async Task RetrieveSeries(string transactionId, IDicomWebClient dicomWeb } if (series.Instances.IsNullOrEmpty()) { - _logger.RetrievingSeriesWithWado(series.SeriesInstanceUid); + _logger.RetrievingSeriesWithWado(series.SeriesInstanceUid!); var files = dicomWebClient.Wado.Retrieve(study.StudyInstanceUid, series.SeriesInstanceUid); await SaveFiles(transactionId, files, retrievedInstance, cancellationToken).ConfigureAwait(false); } else { - await RetrieveInstances(transactionId, dicomWebClient, study.StudyInstanceUid, series, retrievedInstance, cancellationToken).ConfigureAwait(false); + await RetrieveInstances(transactionId, dicomWebClient, study.StudyInstanceUid!, series, retrievedInstance, cancellationToken).ConfigureAwait(false); } } } private async Task RetrieveInstances(string transactionId, IDicomWebClient dicomWebClient, string studyInstanceUid, RequestedSeries series, Dictionary retrievedInstance, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.NullOrWhiteSpace(studyInstanceUid); - Guard.Against.Null(series); - Guard.Against.Null(retrievedInstance); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); + Guard.Against.Null(series, nameof(series)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); var count = retrievedInstance.Count; - foreach (var instance in series.Instances) + foreach (var instance in series.Instances!) { - foreach (var sopInstanceUid in instance.SopInstanceUid) + foreach (var sopInstanceUid in instance.SopInstanceUid!) { if (cancellationToken.IsCancellationRequested) { @@ -530,9 +532,9 @@ private async Task RetrieveInstances(string transactionId, IDicomWebClient dicom private async Task SaveFiles(string transactionId, IAsyncEnumerable files, Dictionary retrievedInstance, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(transactionId); - Guard.Against.Null(files); - Guard.Against.Null(retrievedInstance); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); + Guard.Against.Null(files, nameof(files)); + Guard.Against.Null(retrievedInstance, nameof(retrievedInstance)); var count = retrievedInstance.Count; await foreach (var file in files) @@ -558,14 +560,10 @@ private async Task SaveFiles(string transactionId, IAsyncEnumerable f private static DicomFileStorageMetadata SaveFile(string transactionId, DicomFile file, StudySerieSopUids uids) { - Guard.Against.Null(transactionId); - Guard.Against.Null(file); + Guard.Against.Null(transactionId, nameof(transactionId)); + Guard.Against.Null(file, nameof(file)); - return new DicomFileStorageMetadata(transactionId, uids.Identifier, uids.StudyInstanceUid, uids.SeriesInstanceUid, uids.SopInstanceUid) - { - CalledAeTitle = string.Empty, - Source = transactionId, - }; + return new DicomFileStorageMetadata(transactionId, uids.Identifier, uids.StudyInstanceUid, uids.SeriesInstanceUid, uids.SopInstanceUid, DataService.DicomWeb, transactionId, FileStorageMetadata.IpAddress()); } protected virtual void Dispose(bool disposing) @@ -590,4 +588,4 @@ public void Dispose() #endregion Data Retrieval } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Connectors/IPayloadAssembler.cs b/src/InformaticsGateway/Services/Connectors/IPayloadAssembler.cs index 2a5ebde02..6c72b2daf 100644 --- a/src/InformaticsGateway/Services/Connectors/IPayloadAssembler.cs +++ b/src/InformaticsGateway/Services/Connectors/IPayloadAssembler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,11 @@ * limitations under the License. */ +using System; using System.Threading; using System.Threading.Tasks; using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.Connectors { @@ -26,19 +28,31 @@ namespace Monai.Deploy.InformaticsGateway.Services.Connectors internal interface IPayloadAssembler { /// - /// Queue a new file for the spcified payload bucket. + /// Queue a new file for the specified payload bucket. /// /// The bucket group the file belongs to. /// Path to the file to be added to the payload bucket. - Task Queue(string bucket, FileStorageMetadata file); + /// The service that triggered this queue request + Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin); /// - /// Queue a new file for the spcified payload bucket. + /// Queue a new file for the specified payload bucket. /// /// The bucket group the file belongs to. /// Path to the file to be added to the payload bucket. + /// The service that triggered this queue request /// Number of seconds to wait for additional files. - Task Queue(string bucket, FileStorageMetadata file, uint timeout); + Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin, uint timeout); + + /// + /// Queue a new file for the specified payload bucket. + /// + /// The bucket group the file belongs to. + /// Path to the file to be added to the payload bucket. + /// The service that triggered this queue request + /// Number of seconds to wait for additional files. + /// Cancellation token. + Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin, uint timeout, CancellationToken cancellationToken); /// /// Dequeue a payload from the queue for the message broker to notify subscribers. diff --git a/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs b/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs old mode 100644 new mode 100755 index 2f7ec61e5..f45d5bacc --- a/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadAssembler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,18 +17,23 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Ardalis.GuardClauses; using DotNext.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; -using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.Messaging.Events; + +#nullable enable namespace Monai.Deploy.InformaticsGateway.Services.Connectors { @@ -36,28 +41,29 @@ namespace Monai.Deploy.InformaticsGateway.Services.Connectors /// An in-memory queue for providing any files/DICOM instances received by the Informatics Gateway to /// other internal services. /// - internal sealed partial class PayloadAssembler : IPayloadAssembler, IDisposable + internal sealed partial class PayloadAssembler : IPayloadAssembler, IDisposable, IMonaiService { internal const int DEFAULT_TIMEOUT = 5; - private readonly IOptions _options; private readonly ILogger _logger; private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly CancellationTokenSource _tokenSource; private readonly ConcurrentDictionary> _payloads; private readonly Task _intializedTask; private readonly BlockingCollection _workItems; private readonly System.Timers.Timer _timer; public PayloadAssembler( - IOptions options, ILogger logger, IServiceScopeFactory serviceScopeFactory) { - _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + var scope = _serviceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); // done here to ensure connection on startup - _workItems = new BlockingCollection(); + _workItems = []; + _tokenSource = new CancellationTokenSource(); _payloads = new ConcurrentDictionary>(); _intializedTask = RemovePendingPayloads(); @@ -68,8 +74,14 @@ public PayloadAssembler( }; _timer.Elapsed += OnTimedEvent; _timer.Enabled = true; + + Status = ServiceStatus.Running; } + public string ServiceName { get => nameof(PayloadAssembler); } + + public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; + private async Task RemovePendingPayloads() { _logger.RemovingPendingPayloads(); @@ -86,25 +98,37 @@ private async Task RemovePendingPayloads() /// /// Name of the bucket where the file would be added to /// Instance to be queued - public async Task Queue(string bucket, FileStorageMetadata file) => await Queue(bucket, file, DEFAULT_TIMEOUT).ConfigureAwait(false); + /// The service that triggered this queue request + public async Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin) => await Queue(bucket, file, dataOrigin, DEFAULT_TIMEOUT).ConfigureAwait(false); /// /// Queues a new instance of . /// /// Name of the bucket where the file would be added to /// Instance to be queued + /// The service that triggered this queue request /// Number of seconds the bucket shall wait before sending the payload to be processed. Note: timeout cannot be modified once the bucket is created. - public async Task Queue(string bucket, FileStorageMetadata file, uint timeout) + public async Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin, uint timeout) => await Queue(bucket, file, dataOrigin, timeout, CancellationToken.None).ConfigureAwait(false); + /// + /// Queues a new instance of . + /// + /// Name of the bucket where the file would be added to + /// Instance to be queued + /// The service that triggered this queue request + /// Number of seconds the bucket shall wait before sending the payload to be processed. Note: timeout cannot be modified once the bucket is created. + /// Cancellation token. + public async Task Queue(string bucket, FileStorageMetadata file, DataOrigin dataOrigin, uint timeout, CancellationToken cancellationToken) { - Guard.Against.Null(file); + Guard.Against.Null(file, nameof(file)); await _intializedTask.ConfigureAwait(false); using var _ = _logger.BeginScope(new LoggingDataDictionary() { { "CorrelationId", file.CorrelationId } }); - var payload = await CreateOrGetPayload(bucket, file.CorrelationId, timeout).ConfigureAwait(false); + var payload = await CreateOrGetPayload(bucket, file.CorrelationId, file.WorkflowInstanceId, file.TaskId, dataOrigin, timeout, cancellationToken).ConfigureAwait(false); payload.Add(file); - _logger.FileAddedToBucket(payload.Key, payload.Count); + _logger.FileAddedToBucket(payload.Key, payload.Count, file.PayloadId ?? "null"); + return payload.PayloadId; } /// @@ -118,27 +142,29 @@ public Payload Dequeue(CancellationToken cancellationToken) return _workItems.Take(cancellationToken); } - private async void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) + private async void OnTimedEvent(Object? source, System.Timers.ElapsedEventArgs e) { try { await _intializedTask.ConfigureAwait(false); + _timer.Enabled = false; - _logger.BucketsActive(_payloads.Count); + if (!_payloads.IsEmpty) + { + _logger.BucketsActive(_payloads.Count); + } foreach (var key in _payloads.Keys) { - _logger.BucketElapsedTime(key); - var payload = await _payloads[key].Task.ConfigureAwait(false); + var payload = await _payloads[key].WithCancellation(_tokenSource.Token).ConfigureAwait(false); using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", payload.CorrelationId } }); + + _logger.BucketElapsedTime(key, payload.Timeout, payload.ElapsedTime().TotalSeconds, payload.Files.Count, payload.FilesUploaded, payload.FilesFailedToUpload); + // Wait for timer window closes before sending payload for processing if (payload.HasTimedOut) { - if (payload.AnyUploadFailures()) - { - _payloads.TryRemove(key, out _); - _logger.PayloadRemovedWithFailureUploads(key); - } - else if (payload.IsUploadCompleted()) + if (payload.IsUploadCompleted()) { + _logger.ReceievedAPayload(payload.Elapsed.TotalSeconds, payload.Files.Count, payload.FilesFailedToUpload); if (_payloads.TryRemove(key, out _)) { await QueueBucketForNotification(key, payload).ConfigureAwait(false); @@ -148,6 +174,11 @@ private async void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) _logger.BucketRemoveError(key); } } + else if (payload.IsUploadCompletedWithFailures()) + { + _payloads.TryRemove(key, out _); + _logger.PayloadRemovedWithFailureUploads(key, payload.Count, payload.Files.Count(p => p.IsUploadFailed)); + } } } } @@ -172,6 +203,8 @@ private async Task QueueBucketForNotification(string key, Payload payload) payload.State = Payload.PayloadState.Move; var scope = _serviceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetRequiredService(); + + await repository.UpdateAsync(payload).ConfigureAwait(false); _logger.PayloadSaved(payload.PayloadId); _workItems.Add(payload); @@ -190,23 +223,30 @@ private async Task QueueBucketForNotification(string key, Payload payload) } } - private async Task CreateOrGetPayload(string key, string correationId, uint timeout) + private async Task CreateOrGetPayload(string key, string correlationId, string? workflowInstanceId, string? taskId, Messaging.Events.DataOrigin dataOrigin, uint timeout, CancellationToken cancellationToken = default) { - return await _payloads.GetOrAdd(key, x => new AsyncLazy(async () => - { - var scope = _serviceScopeFactory.CreateScope(); - var repository = scope.ServiceProvider.GetRequiredService(); - var newPayload = new Payload(key, correationId, timeout); - await repository.AddAsync(newPayload).ConfigureAwait(false); - _logger.BucketCreated(key, timeout); - return newPayload; - })); + var payload = _payloads.GetOrAdd(key, x => new AsyncLazy((cancellationToken) => PayloadFactory(key, correlationId, workflowInstanceId, taskId, dataOrigin, timeout, cancellationToken))); + return await payload.WithCancellation(cancellationToken).ConfigureAwait(false); + } + + private async Task PayloadFactory(string key, string correlationId, string? workflowInstanceId, string? taskId, Messaging.Events.DataOrigin dataOrigin, uint timeout, CancellationToken cancellationToken) + { + var newPayload = new Payload(key, correlationId, workflowInstanceId, taskId, dataOrigin, timeout, null); + var scope = _serviceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + + + await repository.AddAsync(newPayload, cancellationToken).ConfigureAwait(false); + _logger.BucketCreated(key, timeout); + return newPayload; } public void Dispose() { + _tokenSource.Cancel(); _payloads.Clear(); _timer.Stop(); + Status = ServiceStatus.Stopped; } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Connectors/PayloadExtensions.cs b/src/InformaticsGateway/Services/Connectors/PayloadExtensions.cs old mode 100644 new mode 100755 index 921d0f1a8..e9edbf8f8 --- a/src/InformaticsGateway/Services/Connectors/PayloadExtensions.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadExtensions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,17 +25,17 @@ internal static class PayloadExtensions { public static bool IsUploadCompleted(this Payload payload) { - return payload.Files.All(p => p.IsUploaded); + return payload.Files.TrueForAll(p => p.IsUploaded); } - public static bool AnyUploadFailures(this Payload payload) + public static bool IsUploadCompletedWithFailures(this Payload payload) { - return payload.Files.Any(p => p.IsUploadFailed); + return (payload.FilesFailedToUpload + payload.FilesUploaded) >= payload.Count; } public static bool IsMoveCompleted(this Payload payload) { - return payload.Files.All(p => p.IsMoveCompleted); + return payload.Files.TrueForAll(p => p.IsMoveCompleted); } public static IReadOnlyList GetWorkflows(this Payload payload) @@ -48,7 +48,7 @@ public static IReadOnlyList GetUploadedFiles(this Payload payl return payload.Files.Select(p => new BlockStorageInfo { Path = p.File.UploadPath, - Metadata = (p is DicomFileStorageMetadata dicom) ? dicom.JsonFile.UploadPath : null, + Metadata = (p is DicomFileStorageMetadata dicom) ? dicom.JsonFile.UploadPath : string.Empty, }).ToList(); } } diff --git a/src/InformaticsGateway/Services/Connectors/PayloadMoveActionHandler.cs b/src/InformaticsGateway/Services/Connectors/PayloadMoveActionHandler.cs index fd08201c3..a9fa1fe79 100644 --- a/src/InformaticsGateway/Services/Connectors/PayloadMoveActionHandler.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadMoveActionHandler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -26,12 +25,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.Storage.API; namespace Monai.Deploy.InformaticsGateway.Services.Connectors { @@ -47,7 +46,6 @@ internal class PayloadMoveActionHandler : IPayloadMoveActionHandler, IDisposable private readonly IOptions _options; private readonly IServiceScope _scope; - private readonly IStorageService _storageService; private bool _disposedValue; public PayloadMoveActionHandler(IServiceScopeFactory serviceScopeFactory, @@ -59,33 +57,35 @@ public PayloadMoveActionHandler(IServiceScopeFactory serviceScopeFactory, _options = options ?? throw new ArgumentNullException(nameof(options)); _scope = _serviceScopeFactory.CreateScope(); - _storageService = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageService)); } public async Task MoveFilesAsync(Payload payload, ActionBlock moveQueue, ActionBlock notificationQueue, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); - Guard.Against.Null(moveQueue); - Guard.Against.Null(notificationQueue); + Guard.Against.Null(payload, nameof(payload)); + Guard.Against.Null(moveQueue, nameof(moveQueue)); + Guard.Against.Null(notificationQueue, nameof(notificationQueue)); + + using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Payload", payload.PayloadId }, { "CorrelationId", payload.CorrelationId } }); if (payload.State != Payload.PayloadState.Move) { - throw new PayloadNotifyException(PayloadNotifyException.FailureReason.IncorrectState); + throw new PayloadNotifyException(PayloadNotifyException.FailureReason.IncorrectState, false); } var stopwatch = Stopwatch.StartNew(); try { - await Move(payload, cancellationToken).ConfigureAwait(false); await NotifyIfCompleted(payload, notificationQueue, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { payload.RetryCount++; var action = await UpdatePayloadState(payload, ex, cancellationToken).ConfigureAwait(false); - if (action == PayloadAction.Updated) + if (action == PayloadAction.Updated && + !await moveQueue.Post(payload, _options.Value.Storage.Retries.RetryDelays.ElementAt(payload.RetryCount - 1)).ConfigureAwait(false)) { - await moveQueue.Post(payload, _options.Value.Storage.Retries.RetryDelays.ElementAt(payload.RetryCount - 1)).ConfigureAwait(false); + + throw new PostPayloadException(Payload.PayloadState.Move, payload); } } finally @@ -97,8 +97,8 @@ public async Task MoveFilesAsync(Payload payload, ActionBlock moveQueue private async Task NotifyIfCompleted(Payload payload, ActionBlock notificationQueue, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); - Guard.Against.Null(notificationQueue); + Guard.Against.Null(payload, nameof(payload)); + Guard.Against.Null(notificationQueue, nameof(notificationQueue)); if (payload.IsMoveCompleted()) { @@ -110,134 +110,37 @@ private async Task NotifyIfCompleted(Payload payload, ActionBlock notif await repository.UpdateAsync(payload, cancellationToken).ConfigureAwait(false); _logger.PayloadSaved(payload.PayloadId); - notificationQueue.Post(payload); - _logger.PayloadReadyToBePublished(payload.PayloadId); - } - else // we should never hit this else block. - { - throw new PayloadNotifyException(PayloadNotifyException.FailureReason.IncompletePayload); - } - } - - private async Task Move(Payload payload, CancellationToken cancellationToken) - { - Guard.Against.Null(payload); - - _logger.MovingFIlesInPayload(payload.PayloadId, _options.Value.Storage.StorageServiceBucketName); - - var options = new ParallelOptions - { - CancellationToken = cancellationToken, - MaxDegreeOfParallelism = _options.Value.Storage.ConcurrentUploads - }; - - var exceptions = new List(); - await Parallel.ForEachAsync(payload.Files, options, async (file, cancellationToke) => - { - try - { - switch (file) - { - case DicomFileStorageMetadata dicom: - if (!string.IsNullOrWhiteSpace(dicom.JsonFile.TemporaryPath)) - { - await MoveFile(payload.PayloadId, dicom.Id, dicom.JsonFile, cancellationToken).ConfigureAwait(false); - } - break; - } - - await MoveFile(payload.PayloadId, file.Id, file.File, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - exceptions.Add(ex); - } - }).ConfigureAwait(false); - - if (exceptions.Any()) - { - throw new AggregateException(exceptions); - } - } - - private async Task MoveFile(Guid payloadId, string identity, StorageObjectMetadata file, CancellationToken cancellationToken) - { - Guard.Against.NullOrWhiteSpace(identity); - Guard.Against.Null(file); - - if (file.IsMoveCompleted) - { - _logger.AlreadyMoved(payloadId, file.UploadPath); - return; - } - - _logger.MovingFileToPayloadDirectory(payloadId, file.UploadPath); - - try - { - await _storageService.CopyObjectAsync( - file.TemporaryBucketName, - file.GetTempStoragPath(_options.Value.Storage.RemoteTemporaryStoragePath), - _options.Value.Storage.StorageServiceBucketName, - file.GetPayloadPath(payloadId), - cancellationToken).ConfigureAwait(false); - - var results = await _storageService.VerifyObjectExistsAsync(_options.Value.Storage.StorageServiceBucketName, new System.Collections.Generic.KeyValuePair(file.UploadPath, file.GetPayloadPath(payloadId))).ConfigureAwait(false); - - if (!results.Key.Equals(file.UploadPath, StringComparison.OrdinalIgnoreCase)) + if (!notificationQueue.Post(payload)) { - _logger.FileMovedVerificationFailure(payloadId, file.UploadPath); - throw new PayloadNotifyException(PayloadNotifyException.FailureReason.MoveFailure); + throw new PostPayloadException(Payload.PayloadState.Notify, payload); } - } - catch (Exception ex) - { - await LogFilesInMinIo(file.TemporaryBucketName, cancellationToken).ConfigureAwait(false); - throw new FileMoveException(file.GetTempStoragPath(_options.Value.Storage.RemoteTemporaryStoragePath), file.UploadPath, ex); - } - try - { - _logger.DeletingFileFromTemporaryBbucket(file.TemporaryBucketName, identity, file.TemporaryPath); - await _storageService.RemoveObjectAsync(file.TemporaryBucketName, file.GetTempStoragPath(_options.Value.Storage.RemoteTemporaryStoragePath), cancellationToken).ConfigureAwait(false); - } - catch (Exception) - { - _logger.ErrorDeletingFileAfterMoveComplete(file.TemporaryBucketName, identity, file.TemporaryPath); - } - finally - { - file.SetMoved(_options.Value.Storage.StorageServiceBucketName); - } - } - - private async Task LogFilesInMinIo(string bucketName, CancellationToken cancellationToken) - { - try - { - var listingResults = await _storageService.ListObjectsAsync(bucketName, recursive: true, cancellationToken: cancellationToken).ConfigureAwait(false); - _logger.FilesFounddOnStorageService(bucketName, listingResults.Count); - foreach (var item in listingResults) - { - _logger.FileFounddOnStorageService(bucketName, item.FilePath); - } + _logger.PayloadReadyToBePublished(payload.PayloadId); } - catch (Exception ex) + else // we should never hit this else block. { - _logger.ErrorListingFilesOnStorageService(ex); + throw new PayloadNotifyException(PayloadNotifyException.FailureReason.IncompletePayload, false); } } private async Task UpdatePayloadState(Payload payload, Exception ex, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); + Guard.Against.Null(payload, nameof(payload)); var scope = _serviceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadRepository)); try { - if (payload.RetryCount > _options.Value.Storage.Retries.DelaysMilliseconds.Length) + if (ex is AggregateException aggregateException && + aggregateException.InnerExceptions.Any(p => (p is PayloadNotifyException payloadNotifyEx) && !payloadNotifyEx.ShallRetry)) + { + _logger.DeletePayloadDueToMissingFiles(payload.PayloadId, ex); + await repository.RemoveAsync(payload, cancellationToken).ConfigureAwait(false); + _logger.PayloadDeleted(payload.PayloadId); + return PayloadAction.Deleted; + } + else if (payload.RetryCount > _options.Value.Storage.Retries.DelaysMilliseconds.Length) { _logger.MoveFailureStopRetry(payload.PayloadId, ex); await repository.RemoveAsync(payload, cancellationToken).ConfigureAwait(false); diff --git a/src/InformaticsGateway/Services/Connectors/PayloadMoveException.cs b/src/InformaticsGateway/Services/Connectors/PayloadMoveException.cs old mode 100644 new mode 100755 index ae71a6817..18293e91a --- a/src/InformaticsGateway/Services/Connectors/PayloadMoveException.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadMoveException.cs @@ -18,10 +18,10 @@ namespace Monai.Deploy.InformaticsGateway.Services.Connectors { - [Serializable] public class PayloadNotifyException : Exception { public FailureReason Reason { get; } + public bool ShallRetry { get; } public enum FailureReason { @@ -29,16 +29,34 @@ public enum FailureReason IncorrectState, IncompletePayload, MoveFailure, + ServiceUnavailable, } - public PayloadNotifyException(FailureReason reason) + public PayloadNotifyException(FailureReason reason) : this(reason, true) + { + } + + public PayloadNotifyException(FailureReason reason, bool shllRetry) { Reason = reason; + ShallRetry = shllRetry; } protected PayloadNotifyException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { throw new NotImplementedException(); } + + public PayloadNotifyException() + { + } + + public PayloadNotifyException(string message) : base(message) + { + } + + public PayloadNotifyException(string message, Exception innerException) : base(message, innerException) + { + } } } diff --git a/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs b/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs old mode 100644 new mode 100755 index f50d5dc19..6b88cb2e5 --- a/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadNotificationActionHandler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ using System; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; @@ -29,6 +30,7 @@ using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.Messaging.API; +using Monai.Deploy.Messaging.Common; using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; @@ -61,8 +63,9 @@ public PayloadNotificationActionHandler(IServiceScopeFactory serviceScopeFactory public async Task NotifyAsync(Payload payload, ActionBlock notificationQueue, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); - Guard.Against.Null(notificationQueue); + _logger.PayloadNotifyAsync(payload.PayloadId.ToString()); + Guard.Against.Null(payload, nameof(payload)); + Guard.Against.Null(notificationQueue, nameof(notificationQueue)); if (payload.State != Payload.PayloadState.Notify) { @@ -71,6 +74,7 @@ public async Task NotifyAsync(Payload payload, ActionBlock notification try { + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "Payload", payload.PayloadId }, { "CorrelationId", payload.CorrelationId } }); await NotifyPayloadReady(payload).ConfigureAwait(false); await DeletePayload(payload, cancellationToken).ConfigureAwait(false); } @@ -80,15 +84,18 @@ public async Task NotifyAsync(Payload payload, ActionBlock notification var action = await UpdatePayloadState(payload, cancellationToken).ConfigureAwait(false); if (action == PayloadAction.Updated) { - await notificationQueue.Post(payload, _options.Value.Messaging.Retries.RetryDelays.ElementAt(payload.RetryCount - 1)).ConfigureAwait(false); _logger.FailedToPublishWorkflowRequest(payload.PayloadId, ex); + if (!await notificationQueue.Post(payload, _options.Value.Messaging.Retries.RetryDelays.ElementAt(payload.RetryCount - 1)).ConfigureAwait(false)) + { + throw new PostPayloadException(Payload.PayloadState.Notify, payload); + } } } } private async Task DeletePayload(Payload payload, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); + Guard.Against.Null(payload, nameof(payload)); var scope = _serviceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadRepository)); @@ -97,8 +104,20 @@ private async Task DeletePayload(Payload payload, CancellationToken cancellation private async Task NotifyPayloadReady(Payload payload) { - Guard.Against.Null(payload); + Guard.Against.Null(payload, nameof(payload)); + if (payload.WorkflowInstanceId.IsNullOrEmpty() is false && payload.TaskId.IsNullOrEmpty() is false) + { + await SendArtifactRecievedEvent(payload).ConfigureAwait(false); + } + else + { + await SendWorkflowRequestEvent(payload).ConfigureAwait(false); + } + } + + private async Task SendWorkflowRequestEvent(Payload payload) + { _logger.GenerateWorkflowRequest(payload.PayloadId); var workflowRequest = new WorkflowRequestEvent @@ -109,9 +128,11 @@ private async Task NotifyPayloadReady(Payload payload) FileCount = payload.Count, CorrelationId = payload.CorrelationId, Timestamp = payload.DateTimeCreated, - CalledAeTitle = payload.CalledAeTitle, - CallingAeTitle = payload.CallingAeTitle, + DataTrigger = payload.DataTrigger, + WorkflowInstanceId = payload.WorkflowInstanceId, + TaskId = payload.TaskId, }; + workflowRequest.DataOrigins.AddRange(payload.DataOrigins); workflowRequest.AddFiles(payload.GetUploadedFiles().AsEnumerable()); @@ -128,12 +149,50 @@ await messageBrokerPublisherService.Publish( _options.Value.Messaging.Topics.WorkflowRequest, message.ToMessage()).ConfigureAwait(false); - _logger.WorkflowRequestPublished(_options.Value.Messaging.Topics.WorkflowRequest, message.MessageId, payload.Elapsed); + _logger.WorkflowRequestPublished(_options.Value.Messaging.Topics.WorkflowRequest, message.MessageId, payload.Elapsed.TotalSeconds); + } + + private async Task SendArtifactRecievedEvent(Payload payload) + { + _logger.GenerateArtifactReceievedRequest(payload.PayloadId); + + var artifiactRecievedEvent = new ArtifactsReceivedEvent + { + Bucket = _options.Value.Storage.StorageServiceBucketName, + PayloadId = payload.PayloadId, + Workflows = payload.GetWorkflows(), + FileCount = payload.Count, + CorrelationId = payload.CorrelationId, + Timestamp = payload.DateTimeCreated, + DataTrigger = payload.DataTrigger, + WorkflowInstanceId = payload.WorkflowInstanceId, + TaskId = payload.TaskId, + }; + artifiactRecievedEvent.DataOrigins.AddRange(payload.DataOrigins); + + _logger.AddingFilesToArtifactsReceivedEvent(JsonSerializer.Serialize(payload)); + + artifiactRecievedEvent.Artifacts = payload.Files.Select(f => new Artifact { Type = f.DataOrigin.ArtifactType, Path = f.File.UploadPath }).ToList(); + + var message = new JsonMessage( + artifiactRecievedEvent, + MessageBrokerConfiguration.InformaticsGatewayApplicationId, + payload.CorrelationId, + string.Empty); + + _logger.PublishingArtifactRecievedRequest(message.MessageId); + + var messageBrokerPublisherService = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IMessageBrokerPublisherService)); + await messageBrokerPublisherService.Publish( + _options.Value.Messaging.Topics.ArtifactRecieved, + message.ToMessage()).ConfigureAwait(false); + + _logger.ArtifactRecievedPublished(_options.Value.Messaging.Topics.ArtifactRecieved, message.MessageId, payload.Elapsed.TotalSeconds); } private async Task UpdatePayloadState(Payload payload, CancellationToken cancellationToken = default) { - Guard.Against.Null(payload); + Guard.Against.Null(payload, nameof(payload)); var scope = _serviceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadRepository)); @@ -182,4 +241,4 @@ public void Dispose() GC.SuppressFinalize(this); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Connectors/PayloadNotificationService.cs b/src/InformaticsGateway/Services/Connectors/PayloadNotificationService.cs old mode 100644 new mode 100755 index ffedb0784..2db9b1d25 --- a/src/InformaticsGateway/Services/Connectors/PayloadNotificationService.cs +++ b/src/InformaticsGateway/Services/Connectors/PayloadNotificationService.cs @@ -62,8 +62,8 @@ internal class PayloadNotificationService : IHostedService, IMonaiService, IDisp private readonly IPayloadNotificationActionHandler _payloadNotificationActionHandler; private readonly IPayloadMoveActionHandler _payloadMoveActionHandler; private readonly CancellationTokenSource _cancellationTokenSource; - private ActionBlock _moveFileQueue; - private ActionBlock _publishQueue; + private ActionBlock? _moveFileQueue; + private ActionBlock? _publishQueue; private bool _disposedValue; public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; @@ -88,16 +88,38 @@ public PayloadNotificationService(IServiceScopeFactory serviceScopeFactory, _cancellationTokenSource = new CancellationTokenSource(); } - public async Task StartAsync(CancellationToken cancellationToken) + public Task StartAsync(CancellationToken cancellationToken) { - _moveFileQueue = new ActionBlock( - MoveActionHandler, - new ExecutionDataflowBlockOptions - { - MaxDegreeOfParallelism = _options.Value.Storage.PayloadProcessThreads, - MaxMessagesPerTask = 1, - CancellationToken = cancellationToken - }); + SetupQueues(cancellationToken); + + var task = Task.Run(async () => + { + await RestoreFromDatabaseAsync(cancellationToken).ConfigureAwait(false); + BackgroundProcessing(cancellationToken); + }, CancellationToken.None); + + Status = ServiceStatus.Running; + _logger.ServiceStarted(ServiceName); + + if (task.IsCompleted) + return task; + + return Task.CompletedTask; + } + + private void SetupQueues(CancellationToken cancellationToken) + { + ResetMoveQueue(cancellationToken); + ResetPublishQueue(cancellationToken); + } + + private void ResetPublishQueue(CancellationToken cancellationToken) + { + if (_publishQueue is not null) + { + _logger.PublishQueueFaulted(_publishQueue.Completion.IsFaulted, _publishQueue.Completion.IsCanceled); + _publishQueue.Complete(); + } _publishQueue = new ActionBlock( NotificationHandler, @@ -107,32 +129,46 @@ public async Task StartAsync(CancellationToken cancellationToken) MaxMessagesPerTask = 1, CancellationToken = cancellationToken }); + } - await RestoreFromDatabaseAsync(cancellationToken).ConfigureAwait(false); - - var task = Task.Run(() => + private void ResetMoveQueue(CancellationToken cancellationToken) + { + if (_moveFileQueue is not null) { - BackgroundProcessing(cancellationToken); - }, CancellationToken.None); - - Status = ServiceStatus.Running; - _logger.ServiceStarted(ServiceName); - - if (task.IsCompleted) - await task.ConfigureAwait(false); + _logger.MoveQueueFaulted(_moveFileQueue.Completion.IsFaulted, _moveFileQueue.Completion.IsCanceled); + _moveFileQueue.Complete(); + } - await Task.CompletedTask.ConfigureAwait(false); + _moveFileQueue = new ActionBlock( + MoveActionHandler, + new ExecutionDataflowBlockOptions + { + MaxDegreeOfParallelism = _options.Value.Storage.PayloadProcessThreads, + MaxMessagesPerTask = 1, + CancellationToken = cancellationToken + }); } private async Task NotificationHandler(Payload payload) { - Guard.Against.Null(payload); - - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Payload", payload.PayloadId }, { "CorrelationId", payload.CorrelationId } }); + Guard.Against.Null(payload, nameof(payload)); try { - await _payloadNotificationActionHandler.NotifyAsync(payload, _publishQueue, _cancellationTokenSource.Token).ConfigureAwait(false); + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { + { "Payload", payload.PayloadId }, + { "WorkflowInstanceId", payload.WorkflowInstanceId ?? "NotSet" }, + { "TaskId", payload.TaskId ?? "NotSet" }, + }); + await _payloadNotificationActionHandler.NotifyAsync(payload, _publishQueue!, _cancellationTokenSource.Token).ConfigureAwait(false); + } + catch (PostPayloadException ex) + { + HandlePostPayloadException(ex); + } + catch (PostPayloadException ex) + { + HandlePostPayloadException(ex); } catch (Exception ex) { @@ -150,13 +186,19 @@ private async Task NotificationHandler(Payload payload) private async Task MoveActionHandler(Payload payload) { - Guard.Against.Null(payload); - - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Payload", payload.PayloadId }, { "CorrelationId", payload.CorrelationId } }); + Guard.Against.Null(payload, nameof(payload)); try { - await _payloadMoveActionHandler.MoveFilesAsync(payload, _moveFileQueue, _publishQueue, _cancellationTokenSource.Token).ConfigureAwait(false); + await _payloadMoveActionHandler.MoveFilesAsync(payload, _moveFileQueue!, _publishQueue!, _cancellationTokenSource.Token).ConfigureAwait(false); + } + catch (PostPayloadException ex) + { + HandlePostPayloadException(ex); + } + catch (PostPayloadException ex) + { + HandlePostPayloadException(ex); } catch (Exception ex) { @@ -172,17 +214,47 @@ private async Task MoveActionHandler(Payload payload) } } + private void HandlePostPayloadException(PostPayloadException ex) + { + Guard.Against.Null(ex, nameof(ex)); + + if (ex.TargetQueue == Payload.PayloadState.Move) + { + ResetIfFaultedOrCancelled(_moveFileQueue!, ResetMoveQueue, CancellationToken.None); + if (!_moveFileQueue!.Post(ex.Payload!)) + { + _logger.ErrorPostingJobToMovePayloadsQueue(); + } + } + else if (ex.TargetQueue == Payload.PayloadState.Notify) + { + ResetIfFaultedOrCancelled(_publishQueue!, ResetPublishQueue, CancellationToken.None); + if (!_publishQueue!.Post(ex.Payload!)) + { + _logger.ErrorPostingJobToPublishPayloadsQueue(); + } + } + } + private void BackgroundProcessing(CancellationToken cancellationToken) { _logger.ServiceRunning(ServiceName); while (!cancellationToken.IsCancellationRequested) { - Payload payload = null; + ResetIfFaultedOrCancelled(_moveFileQueue!, ResetMoveQueue, cancellationToken); + ResetIfFaultedOrCancelled(_publishQueue!, ResetPublishQueue, cancellationToken); + + Payload? payload = null; try { payload = _payloadAssembler.Dequeue(cancellationToken); - _moveFileQueue.Post(payload); + using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Payload ID", payload.PayloadId }, { "CorrelationId", payload.CorrelationId } }); + + while (!_moveFileQueue!.Post(payload)) + { + ResetIfFaultedOrCancelled(_moveFileQueue!, ResetMoveQueue, cancellationToken); + } _logger.PayloadQueuedForProcessing(payload.PayloadId, ServiceName); } catch (OperationCanceledException ex) @@ -202,6 +274,17 @@ private void BackgroundProcessing(CancellationToken cancellationToken) _logger.ServiceCancelled(ServiceName); } + private static void ResetIfFaultedOrCancelled(ActionBlock queue, Action resetFunction, CancellationToken cancellationToken) + { + Guard.Against.Null(queue, nameof(queue)); + Guard.Against.Null(resetFunction, nameof(resetFunction)); + + if (queue.Completion.IsCanceledOrFaulted()) + { + resetFunction(cancellationToken); + } + } + private async Task RestoreFromDatabaseAsync(CancellationToken cancellationToken) { _logger.StartupRestoreFromDatabase(); @@ -214,11 +297,14 @@ private async Task RestoreFromDatabaseAsync(CancellationToken cancellationToken) { if (payload.State == Payload.PayloadState.Move) { - _moveFileQueue.Post(payload); + if (!_moveFileQueue!.Post(payload)) + { + _logger.ErrorPostingJobToMovePayloadsQueue(); + } } - else if (payload.State == Payload.PayloadState.Notify) + else if (payload.State == Payload.PayloadState.Notify && !_publishQueue!.Post(payload)) { - _publishQueue.Post(payload); + _logger.ErrorPostingJobToPublishPayloadsQueue(); } } _logger.RestoredFromDatabase(payloads.Count); @@ -228,8 +314,8 @@ public async Task StopAsync(CancellationToken cancellationToken) { _logger.ServiceStopping(ServiceName); _cancellationTokenSource.Cancel(); - _moveFileQueue.Complete(); - _publishQueue.Complete(); + _moveFileQueue!.Complete(); + _publishQueue!.Complete(); Status = ServiceStatus.Stopped; _logger.ServiceStopPending(ServiceName); @@ -243,6 +329,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { + _cancellationTokenSource.Dispose(); _scope.Dispose(); } diff --git a/src/InformaticsGateway/Services/DicomWeb/ContentTypes.cs b/src/InformaticsGateway/Services/DicomWeb/ContentTypes.cs index 7bb731f23..4fc30edea 100644 --- a/src/InformaticsGateway/Services/DicomWeb/ContentTypes.cs +++ b/src/InformaticsGateway/Services/DicomWeb/ContentTypes.cs @@ -22,6 +22,7 @@ internal static class ContentTypes public const string ApplicationDicomJson = "application/dicom+json"; public const string ApplicationDicomXml = "application/dicom+xml"; public const string ApplicationOctetStream = "application/octet-stream"; + public const string ApplicationJson = "application/json"; public const string MultipartRelated = "multipart/related"; diff --git a/src/InformaticsGateway/Services/DicomWeb/ConvertStreamException.cs b/src/InformaticsGateway/Services/DicomWeb/ConvertStreamException.cs index fd411f283..04dc14750 100644 --- a/src/InformaticsGateway/Services/DicomWeb/ConvertStreamException.cs +++ b/src/InformaticsGateway/Services/DicomWeb/ConvertStreamException.cs @@ -15,11 +15,9 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Services.DicomWeb { - [Serializable] public class ConvertStreamException : Exception { public ConvertStreamException() @@ -33,9 +31,5 @@ public ConvertStreamException(string message) : base(message) public ConvertStreamException(string message, Exception innerException) : base(message, innerException) { } - - protected ConvertStreamException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Services/DicomWeb/DicomInstanceReaderBase.cs b/src/InformaticsGateway/Services/DicomWeb/DicomInstanceReaderBase.cs old mode 100644 new mode 100755 index 3452dbf85..a31796477 --- a/src/InformaticsGateway/Services/DicomWeb/DicomInstanceReaderBase.cs +++ b/src/InformaticsGateway/Services/DicomWeb/DicomInstanceReaderBase.cs @@ -51,10 +51,10 @@ protected DicomInstanceReaderBase( protected static void ValidateSupportedMediaTypes(string contentType, out MediaTypeHeaderValue mediaTypeHeaderValue, params string[] contentTypes) { - Guard.Against.Null(contentType); + Guard.Against.Null(contentType, nameof(contentType)); if (MediaTypeHeaderValue.TryParse(contentType, out var mediaType) && - contentTypes.Any(p => p.Equals(mediaType.MediaType.ToString(), StringComparison.OrdinalIgnoreCase))) + contentTypes.ToList().Exists(p => p.Equals(mediaType.MediaType.ToString(), StringComparison.OrdinalIgnoreCase))) { mediaTypeHeaderValue = mediaType; return; @@ -65,8 +65,8 @@ protected static void ValidateSupportedMediaTypes(string contentType, out MediaT protected async Task ConvertStream(HttpContext httpContext, Stream sourceStream, CancellationToken cancellationToken = default) { - Guard.Against.Null(httpContext); - Guard.Against.Null(sourceStream); + Guard.Against.Null(httpContext, nameof(httpContext)); + Guard.Against.Null(sourceStream, nameof(sourceStream)); Stream seekableStream; if (!sourceStream.CanSeek) diff --git a/src/InformaticsGateway/Services/DicomWeb/IStowService.cs b/src/InformaticsGateway/Services/DicomWeb/IStowService.cs index 5d377250b..5b4e77ebf 100644 --- a/src/InformaticsGateway/Services/DicomWeb/IStowService.cs +++ b/src/InformaticsGateway/Services/DicomWeb/IStowService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,6 @@ namespace Monai.Deploy.InformaticsGateway.Services.DicomWeb { internal interface IStowService { - Task StoreAsync(HttpRequest request, string studyInstanceUid, string workflowName, string correlationId, CancellationToken cancellationToken); + Task StoreAsync(HttpRequest request, string? studyInstanceUid, string? aet, string? workflowName, string correlationId, CancellationToken cancellationToken); } } diff --git a/src/InformaticsGateway/Services/DicomWeb/IStreamsWriter.cs b/src/InformaticsGateway/Services/DicomWeb/IStreamsWriter.cs index 7f9cbb2df..a4304a49c 100644 --- a/src/InformaticsGateway/Services/DicomWeb/IStreamsWriter.cs +++ b/src/InformaticsGateway/Services/DicomWeb/IStreamsWriter.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,42 +18,47 @@ using System.Collections.Generic; using System.IO; using System.IO.Abstractions; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Ardalis.GuardClauses; using FellowOakDicom; using FellowOakDicom.Network; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.DicomWeb { internal interface IStreamsWriter { - Task Save(IList streams, string studyInstanceUid, string workflowName, string correlationId, string dataSource, CancellationToken cancellationToken = default); + Task Save(IList streams, string? studyInstanceUid, VirtualApplicationEntity? virtualApplicationEntity, string? workflowName, string correlationId, string dataSource, CancellationToken cancellationToken = default); } internal class StreamsWriter : IStreamsWriter { private readonly ILogger _logger; private readonly IFileSystem _fileSystem; + private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IObjectUploadQueue _uploadQueue; private readonly IDicomToolkit _dicomToolkit; private readonly IPayloadAssembler _payloadAssembler; private readonly IOptions _configuration; private readonly DicomDataset _resultDicomDataset; private int _failureCount; - private int _storedCount; public StreamsWriter( + IServiceScopeFactory serviceScopeFactory, IObjectUploadQueue fileStore, IDicomToolkit dicomToolkit, IPayloadAssembler payloadAssembler, @@ -61,6 +66,7 @@ public StreamsWriter( ILogger logger, IFileSystem fileSystem) { + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _uploadQueue = fileStore ?? throw new ArgumentNullException(nameof(fileStore)); _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); _payloadAssembler = payloadAssembler ?? throw new ArgumentNullException(nameof(payloadAssembler)); @@ -69,14 +75,40 @@ public StreamsWriter( _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _resultDicomDataset = new DicomDataset(); _failureCount = 0; - _storedCount = 0; } - public async Task Save(IList streams, string studyInstanceUid, string workflowName, string correlationId, string dataSource, CancellationToken cancellationToken = default) + public async Task Save(IList streams, string? studyInstanceUid, VirtualApplicationEntity? virtualApplicationEntity, string? workflowName, string correlationId, string dataSource, CancellationToken cancellationToken = default) { - Guard.Against.NullOrEmpty(streams); - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(dataSource); + if (streams.IsNullOrEmpty()) + { + return new StowResult { StatusCode = StatusCodes.Status204NoContent }; + } + + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(dataSource, nameof(dataSource)); + + var inputDataPlugInEngine = _serviceScopeFactory.CreateScope().ServiceProvider.GetService(); + string[]? workflows = null; + + if (virtualApplicationEntity is not null) + { + inputDataPlugInEngine!.Configure(virtualApplicationEntity.PlugInAssemblies); + workflows = virtualApplicationEntity.Workflows.ToArray(); + } + else + { + inputDataPlugInEngine!.Configure(_configuration.Value.DicomWeb.PlugInAssemblies); + } + + // If a workflow is specified, it will overwrite ones specified in a virtual AE. + if (!string.IsNullOrWhiteSpace(workflowName)) + { + workflows = new[] { workflowName }; + } + else if (virtualApplicationEntity?.Workflows.Any() ?? false) + { + workflows = virtualApplicationEntity.Workflows.ToArray(); + } foreach (var stream in streams) { @@ -88,7 +120,7 @@ public async Task Save(IList streams, string studyInstanceUi _logger.ZeroLengthDicomWebStowStream(); continue; } - await SaveInstance(stream, studyInstanceUid, workflowName, correlationId, dataSource, cancellationToken).ConfigureAwait(false); + await SaveInstance(stream, studyInstanceUid, inputDataPlugInEngine, correlationId, dataSource, virtualApplicationEntity?.Name ?? "default", workflows).ConfigureAwait(false); } catch (Exception ex) { @@ -97,11 +129,6 @@ public async Task Save(IList streams, string studyInstanceUi } } - if (_storedCount == 0 && _failureCount == 0) - { - return new StowResult { StatusCode = StatusCodes.Status204NoContent }; - } - return new StowResult { StatusCode = GetStatusCode(streams.Count), @@ -125,11 +152,11 @@ private int GetStatusCode(int instancesReceived) } } - private async Task SaveInstance(Stream stream, string studyInstanceUid, string workflowName, string correlationId, string dataSource, CancellationToken cancellationToken = default) + private async Task SaveInstance(Stream stream, string? studyInstanceUid, IInputDataPlugInEngine inputDataPlugInEngine, string correlationId, string dataSource, string endpointName, params string[]? workflows) { - Guard.Against.Null(stream); - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(dataSource); + Guard.Against.Null(stream, nameof(stream)); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(dataSource, nameof(dataSource)); stream.Seek(0, SeekOrigin.Begin); DicomFile dicomFile; @@ -158,30 +185,30 @@ private async Task SaveInstance(Stream stream, string studyInstanceUid, string w return; } - var dicomInfo = new DicomFileStorageMetadata(correlationId, uids.Identifier, uids.StudyInstanceUid, uids.SeriesInstanceUid, uids.SopInstanceUid) - { - CalledAeTitle = string.Empty, - Source = dataSource, - }; + var dicomInfo = new DicomFileStorageMetadata(correlationId, uids.Identifier, uids.StudyInstanceUid, uids.SeriesInstanceUid, uids.SopInstanceUid, Messaging.Events.DataService.DicomWeb, dataSource, endpointName); - if (!string.IsNullOrWhiteSpace(workflowName)) + if (!workflows.IsNullOrEmpty()) { - dicomInfo.SetWorkflows(workflowName); + dicomInfo.SetWorkflows(workflows!); } + var result = await inputDataPlugInEngine.ExecutePlugInsAsync(dicomFile, dicomInfo).ConfigureAwait(false); + dicomFile = result.Item1; + dicomInfo = result.Item2 as DicomFileStorageMetadata; + + // for DICOMweb, use correlation ID as the grouping key + var payloadId = await _payloadAssembler.Queue(correlationId, dicomInfo!, new DataOrigin { DataService = DataService.DicomWeb, Source = dataSource, Destination = endpointName }, _configuration.Value.DicomWeb.Timeout).ConfigureAwait(false); + dicomInfo!.PayloadId = payloadId.ToString(); + await dicomInfo.SetDataStreams(dicomFile, dicomFile.ToJson(_configuration.Value.Dicom.WriteDicomJson, _configuration.Value.Dicom.ValidateDicomOnSerialization), _configuration.Value.Storage.TemporaryDataStorage, _fileSystem, _configuration.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); _uploadQueue.Queue(dicomInfo); - // for DICOMweb, use correlation ID as the grouping key - await _payloadAssembler.Queue(correlationId, dicomInfo, _configuration.Value.DicomWeb.Timeout).ConfigureAwait(false); _logger.QueuedStowInstance(); AddSuccess(null, uids); - - _storedCount++; } - private void AddSuccess(DicomStatus warningStatus = null, StudySerieSopUids uids = default) + private void AddSuccess(DicomStatus? warningStatus = null, StudySerieSopUids? uids = default) { if (!_resultDicomDataset.TryGetSequence(DicomTag.ReferencedSOPSequence, out var referencedSopSequence)) { @@ -208,9 +235,9 @@ private void AddSuccess(DicomStatus warningStatus = null, StudySerieSopUids uids } /// - private void AddFailure(DicomStatus dicomStatus, StudySerieSopUids uids = default) + private void AddFailure(DicomStatus dicomStatus, StudySerieSopUids? uids = default) { - Guard.Against.Null(dicomStatus); + Guard.Against.Null(dicomStatus, nameof(dicomStatus)); _failureCount++; diff --git a/src/InformaticsGateway/Services/DicomWeb/MultipartDicomInstanceReader.cs b/src/InformaticsGateway/Services/DicomWeb/MultipartDicomInstanceReader.cs old mode 100644 new mode 100755 index efb14a834..318b25dc0 --- a/src/InformaticsGateway/Services/DicomWeb/MultipartDicomInstanceReader.cs +++ b/src/InformaticsGateway/Services/DicomWeb/MultipartDicomInstanceReader.cs @@ -40,8 +40,8 @@ public MultipartDicomInstanceReader(InformaticsGatewayConfiguration configuratio public async Task> GetStreams(HttpRequest request, MediaTypeHeaderValue mediaTypeHeaderValue, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.Null(mediaTypeHeaderValue); + Guard.Against.Null(request, nameof(request)); + Guard.Against.Null(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue)); var boundary = HeaderUtilities.RemoveQuotes(mediaTypeHeaderValue.Boundary).ToString(); @@ -78,7 +78,7 @@ public async Task> GetStreams(HttpRequest request, MediaTypeHeader }; var streams = new List(); - MultipartSection multipartSection; + MultipartSection? multipartSection; while ((multipartSection = await multipartReader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false)) is not null) { var contentType = multipartSection.ContentType; diff --git a/src/InformaticsGateway/Services/DicomWeb/SingleDicomInstanceReader.cs b/src/InformaticsGateway/Services/DicomWeb/SingleDicomInstanceReader.cs index d9f8b5d8a..4b5b13a30 100644 --- a/src/InformaticsGateway/Services/DicomWeb/SingleDicomInstanceReader.cs +++ b/src/InformaticsGateway/Services/DicomWeb/SingleDicomInstanceReader.cs @@ -37,8 +37,8 @@ public SingleDicomInstanceReader(InformaticsGatewayConfiguration configuration, public async Task> GetStreams(HttpRequest request, MediaTypeHeaderValue mediaTypeHeaderValue, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.Null(mediaTypeHeaderValue); + Guard.Against.Null(request, nameof(request)); + Guard.Against.Null(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue)); try { diff --git a/src/InformaticsGateway/Services/DicomWeb/StowResult.cs b/src/InformaticsGateway/Services/DicomWeb/StowResult.cs old mode 100644 new mode 100755 index 20747a96f..b290f1c1a --- a/src/InformaticsGateway/Services/DicomWeb/StowResult.cs +++ b/src/InformaticsGateway/Services/DicomWeb/StowResult.cs @@ -21,6 +21,6 @@ namespace Monai.Deploy.InformaticsGateway.Services.DicomWeb public class StowResult { public int StatusCode { get; internal set; } - public DicomDataset Data { get; internal set; } + public DicomDataset? Data { get; internal set; } } } diff --git a/src/InformaticsGateway/Services/DicomWeb/StowService.cs b/src/InformaticsGateway/Services/DicomWeb/StowService.cs index 6ec038ca9..d4cbc61b7 100644 --- a/src/InformaticsGateway/Services/DicomWeb/StowService.cs +++ b/src/InformaticsGateway/Services/DicomWeb/StowService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,10 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; +using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; namespace Monai.Deploy.InformaticsGateway.Services.DicomWeb @@ -37,6 +39,7 @@ internal class StowService : IStowService private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IOptions _configuration; private readonly ILogger _logger; + private readonly IVirtualApplicationEntityRepository _repository; public StowService(IServiceScopeFactory serviceScopeFactory, IOptions configuration) { @@ -45,12 +48,25 @@ public StowService(IServiceScopeFactory serviceScopeFactory, IOptions>() ?? throw new ServiceNotFoundException(nameof(ILogger)); + _repository = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IVirtualApplicationEntityRepository)); } - public async Task StoreAsync(HttpRequest request, string studyInstanceUid, string workflowName, string correlationId, CancellationToken cancellationToken) + public async Task StoreAsync(HttpRequest request, string? studyInstanceUid, string? aet, string? workflowName, string correlationId, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.Null(request, nameof(request)); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + + VirtualApplicationEntity? vae = null; + + if (!string.IsNullOrWhiteSpace(aet)) + { + vae = await ValidateVirtualAet(aet).ConfigureAwait(false); + + if (vae is null) + { + throw new ApplicationEntityNotFoundException($"{aet} cannot be found and may be not configured."); + } + } if (!string.IsNullOrWhiteSpace(studyInstanceUid)) { @@ -71,12 +87,12 @@ public async Task StoreAsync(HttpRequest request, string studyInstan var streamsWriter = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStreamsWriter)); _logger.SavingStream(streams.Count); - return await streamsWriter.Save(streams, studyInstanceUid, workflowName, correlationId, request.HttpContext.Connection.RemoteIpAddress.ToString(), cancellationToken).ConfigureAwait(false); + return await streamsWriter.Save(streams, studyInstanceUid, vae, workflowName, correlationId, request.HttpContext.Connection.RemoteIpAddress!.ToString(), cancellationToken).ConfigureAwait(false); } private IStowRequestReader GetRequestReader(MediaTypeHeaderValue mediaTypeHeaderValue) { - Guard.Against.Null(mediaTypeHeaderValue); + Guard.Against.Null(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue)); var scope = _serviceScopeFactory.CreateScope(); var fileSystem = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IFileSystem)); @@ -94,5 +110,10 @@ private IStowRequestReader GetRequestReader(MediaTypeHeaderValue mediaTypeHeader throw new UnsupportedContentTypeException($"Media type of '{mediaTypeHeaderValue.MediaType}' is not supported."); } + + private async Task ValidateVirtualAet(string aet) + { + return await _repository.FindByAeTitleAsync(aet).ConfigureAwait(false); + } } } diff --git a/src/InformaticsGateway/Services/Export/DicomWebExportService.cs b/src/InformaticsGateway/Services/Export/DicomWebExportService.cs old mode 100644 new mode 100755 index 8226b16a8..9eca29da2 --- a/src/InformaticsGateway/Services/Export/DicomWebExportService.cs +++ b/src/InformaticsGateway/Services/Export/DicomWebExportService.cs @@ -26,7 +26,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; @@ -35,6 +35,7 @@ using Monai.Deploy.InformaticsGateway.DicomWeb.Client.API; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.Messaging.Common; using Monai.Deploy.Messaging.Events; using Polly; @@ -44,12 +45,11 @@ internal class DicomWebExportService : ExportServiceBase { private readonly ILoggerFactory _loggerFactory; private readonly IHttpClientFactory _httpClientFactory; - private readonly IServiceScopeFactory _serviceScopeFactory; private readonly ILogger _logger; private readonly IOptions _configuration; private readonly IDicomToolkit _dicomToolkit; - protected override int Concurrency { get; } + protected override ushort Concurrency { get; } public override string RoutingKey { get; } public override string ServiceName => "DICOMweb Export Service"; @@ -60,11 +60,10 @@ public DicomWebExportService( ILogger logger, IOptions configuration, IDicomToolkit dicomToolkit) - : base(logger, configuration, serviceScopeFactory) + : base(logger, configuration, serviceScopeFactory, dicomToolkit) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); - _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); @@ -73,11 +72,15 @@ public DicomWebExportService( Concurrency = configuration.Value.DicomWeb.MaximumNumberOfConnection; } + protected override async Task ProcessMessage(MessageReceivedEventArgs eventArgs) + { + await BaseProcessMessage(eventArgs); + } protected override async Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken) { - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId }, { "Filename", exportRequestData.Filename } }); + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId }, { "Filename", exportRequestData.Filename } }); - using var scope = _serviceScopeFactory.CreateScope(); + using var scope = ServiceScopeFactory.CreateScope(); var repository = scope.ServiceProvider.GetRequiredService(); foreach (var transaction in exportRequestData.Destinations) @@ -90,9 +93,9 @@ protected override async Task ExportDataBlockCallback( private async Task HandleTransaction(ExportRequestDataMessage exportRequestData, IInferenceRequestRepository repository, string transaction, CancellationToken cancellationToken) { - Guard.Against.Null(exportRequestData); - Guard.Against.Null(repository); - Guard.Against.NullOrWhiteSpace(transaction); + Guard.Against.Null(exportRequestData, nameof(exportRequestData)); + Guard.Against.Null(repository, nameof(repository)); + Guard.Against.NullOrWhiteSpace(transaction, nameof(transaction)); var inferenceRequest = await repository.GetInferenceRequestAsync(transaction, cancellationToken).ConfigureAwait(false); if (inferenceRequest is null) @@ -103,7 +106,7 @@ private async Task HandleTransaction(ExportRequestDataMessage exportRequestData, return; } - var destinations = inferenceRequest.OutputResources.Where(p => p.Interface == InputInterfaceType.DicomWeb); + var destinations = inferenceRequest.OutputResources!.Where(p => p.Interface == InputInterfaceType.DicomWeb); if (!destinations.Any()) { @@ -115,12 +118,12 @@ private async Task HandleTransaction(ExportRequestDataMessage exportRequestData, foreach (var destination in destinations) { - var authenticationHeader = AuthenticationHeaderValueExtensions.ConvertFrom(destination.ConnectionDetails.AuthType, destination.ConnectionDetails.AuthId); + var authenticationHeader = AuthenticationHeaderValueExtensions.ConvertFrom(destination.ConnectionDetails.AuthType, destination.ConnectionDetails.AuthId!); var dicomWebClient = new DicomWebClient(_httpClientFactory.CreateClient("dicomweb"), _loggerFactory.CreateLogger()); - dicomWebClient.ConfigureServiceUris(new Uri(destination.ConnectionDetails.Uri, UriKind.Absolute)); + dicomWebClient.ConfigureServiceUris(new Uri(destination.ConnectionDetails.Uri!, UriKind.Absolute)); dicomWebClient.ConfigureAuthentication(authenticationHeader); - _logger.ExportToDicomWeb(destination.ConnectionDetails.Uri); + _logger.ExportToDicomWeb(destination.ConnectionDetails.Uri!); await ExportToDicomWebDestination(dicomWebClient, exportRequestData, destination, cancellationToken).ConfigureAwait(false); } } @@ -148,7 +151,7 @@ await Policy _configuration.Value.Export.Retries.RetryDelays, (exception, timeSpan, retryCount, context) => { - _logger.ErrorExportingDicomWebWithRetry(destination.ConnectionDetails.Uri, timeSpan, retryCount, exception); + _logger.ErrorExportingDicomWebWithRetry(destination.ConnectionDetails.Uri!, timeSpan, retryCount, exception); }) .ExecuteAsync(async () => { @@ -166,7 +169,7 @@ await Policy private void CheckAndLogResult(DicomWebResponse result) { - Guard.Against.Null(result); + Guard.Against.Null(result, nameof(result)); switch (result.StatusCode) { case System.Net.HttpStatusCode.OK: @@ -174,7 +177,7 @@ private void CheckAndLogResult(DicomWebResponse result) break; default: - throw new ServiceException("Failed to export to destination."); + throw new InformaticsGateway.Common.ServiceException("Failed to export to destination."); } } } diff --git a/src/InformaticsGateway/Services/Export/ExportRequestEventDetails.cs b/src/InformaticsGateway/Services/Export/ExportRequestEventDetails.cs old mode 100644 new mode 100755 index 2bbda686a..66a704e63 --- a/src/InformaticsGateway/Services/Export/ExportRequestEventDetails.cs +++ b/src/InformaticsGateway/Services/Export/ExportRequestEventDetails.cs @@ -29,12 +29,51 @@ public ExportRequestEventDetails(ExportRequestEvent exportRequest) ExportTaskId = exportRequest.ExportTaskId; Files = new List(exportRequest.Files); Destinations = new string[exportRequest.Destinations.Length]; - Array.Copy(exportRequest.Destinations, Destinations, exportRequest.Destinations.Length); + //Array.Copy(exportRequest.Destinations, Destinations, exportRequest.Destinations.Length); exportRequest.Destinations.CopyTo(Destinations, 0); DeliveryTag = exportRequest.DeliveryTag; MessageId = exportRequest.MessageId; WorkflowInstanceId = exportRequest.WorkflowInstanceId; - AddErrorMessages(exportRequest.ErrorMessages); + PayloadId = exportRequest.PayloadId; + + PluginAssemblies.AddRange(exportRequest.PluginAssemblies); + ErrorMessages.AddRange(exportRequest.ErrorMessages); + + StartTime = DateTimeOffset.UtcNow; + } + + public ExportRequestEventDetails(ExternalAppRequestEvent externalAppRequest) + { + CorrelationId = externalAppRequest.CorrelationId; + ExportTaskId = externalAppRequest.ExportTaskId; + Files = new List(externalAppRequest.Files); + Destinations = externalAppRequest.Targets.Select(t => t.Destination).ToArray(); + DeliveryTag = externalAppRequest.DeliveryTag; + MessageId = externalAppRequest.MessageId; + WorkflowInstanceId = externalAppRequest.WorkflowInstanceId; + PayloadId = externalAppRequest.DestinationFolder; + + PluginAssemblies.AddRange(externalAppRequest.PluginAssemblies); + ErrorMessages.AddRange(externalAppRequest.ErrorMessages); + + StartTime = DateTimeOffset.UtcNow; + } + + /// + /// Gets the time the export request received. + /// + public DateTimeOffset StartTime { get; } + + + /// + /// Gets time between now and . + /// + public TimeSpan Duration + { + get + { + return DateTimeOffset.UtcNow.Subtract(StartTime); + } } /// @@ -50,11 +89,16 @@ public ExportRequestEventDetails(ExportRequestEvent exportRequest) /// /// Gets whether the export task is completed or not based on file count. /// - public bool IsCompleted { get { return (SucceededFiles + FailedFiles) == Files.Count(); } } + public bool IsCompleted + { + get + { + return (SucceededFiles + FailedFiles) == Files.Count(); + } + } public Dictionary FileStatuses { get; private set; } = new Dictionary(); - public ExportStatus Status { get diff --git a/src/InformaticsGateway/Services/Export/ExportServiceBase.cs b/src/InformaticsGateway/Services/Export/ExportServiceBase.cs old mode 100644 new mode 100755 index defb1976d..00b5f4b0e --- a/src/InformaticsGateway/Services/Export/ExportServiceBase.cs +++ b/src/InformaticsGateway/Services/Export/ExportServiceBase.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,14 +23,18 @@ using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; using Ardalis.GuardClauses; +using FellowOakDicom.Network; +using FellowOakDicom; +using FellowOakDicom.Network.Client; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Storage; @@ -40,49 +44,60 @@ using Monai.Deploy.Messaging.Messages; using Monai.Deploy.Storage.API; using Polly; +using System.Net.Sockets; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Services.Export { public abstract class ExportServiceBase : IHostedService, IMonaiService, IDisposable { - private static readonly object SyncRoot = new(); + protected static readonly object SyncRoot = new(); - internal event EventHandler ReportActionCompleted; + internal event EventHandler? ReportActionCompleted; private readonly CancellationTokenSource _cancellationTokenSource; private readonly ILogger _logger; - private readonly IServiceScopeFactory _serviceScopeFactory; + protected readonly IServiceScopeFactory ServiceScopeFactory; private readonly InformaticsGatewayConfiguration _configuration; - private readonly IMessageBrokerSubscriberService _messageSubscriber; - private readonly IMessageBrokerPublisherService _messagePublisher; + protected readonly IMessageBrokerSubscriberService MessageSubscriber; + protected readonly IMessageBrokerPublisherService MessagePublisher; private readonly IServiceScope _scope; - private readonly Dictionary _exportRequests; + protected readonly Dictionary ExportRequests; private readonly IStorageInfoProvider _storageInfoProvider; private bool _disposedValue; + private ulong _activeWorkers = 0; + private readonly IDicomToolkit _dicomToolkit; public abstract string RoutingKey { get; } - protected abstract int Concurrency { get; } + protected abstract ushort Concurrency { get; } public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; public abstract string ServiceName { get; } + protected string ExportCompleteTopic { get; set; } + /// /// Override the ExportDataBlockCallback method to customize export logic. /// Must update State to either Succeeded or Failed. /// - /// + /// /// /// protected abstract Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken); + protected abstract Task ProcessMessage(MessageReceivedEventArgs eventArgs); + protected ExportServiceBase( ILogger logger, IOptions configuration, - IServiceScopeFactory serviceScopeFactory) + IServiceScopeFactory serviceScopeFactory, + IDicomToolkit dicomToolkit) { _cancellationTokenSource = new CancellationTokenSource(); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); - _scope = _serviceScopeFactory.CreateScope(); + ServiceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + _scope = ServiceScopeFactory.CreateScope(); + _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); + if (configuration is null) { @@ -91,13 +106,14 @@ protected ExportServiceBase( _configuration = configuration.Value; - _messageSubscriber = _scope.ServiceProvider.GetRequiredService(); - _messagePublisher = _scope.ServiceProvider.GetRequiredService(); + ExportCompleteTopic = _configuration.Messaging.Topics.ExportComplete; + MessageSubscriber = _scope.ServiceProvider.GetRequiredService(); + MessagePublisher = _scope.ServiceProvider.GetRequiredService(); _storageInfoProvider = _scope.ServiceProvider.GetRequiredService(); - _exportRequests = new Dictionary(); + ExportRequests = new Dictionary(); - _messageSubscriber.OnConnectionError += (sender, args) => + MessageSubscriber.OnConnectionError += (sender, args) => { _logger.MessagingServiceErrorRecover(args.ErrorMessage); SetupPolling(); @@ -113,99 +129,177 @@ public Task StartAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) + public async Task StopAsync(CancellationToken cancellationToken) { _cancellationTokenSource.Cancel(); _logger.ServiceStopping(ServiceName); Status = ServiceStatus.Stopped; - return Task.CompletedTask; +#pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods + await Task.Delay(250).ConfigureAwait(false); +#pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods + _cancellationTokenSource.Dispose(); } private void SetupPolling() { - _messageSubscriber.Subscribe(RoutingKey, RoutingKey, OnMessageReceivedCallback); + MessageSubscriber.SubscribeAsync(RoutingKey, RoutingKey, OnMessageReceivedCallback, prefetchCount: Concurrency); _logger.ExportEventSubscription(ServiceName, RoutingKey); } - private void OnMessageReceivedCallback(MessageReceivedEventArgs eventArgs) + protected virtual async Task OnMessageReceivedCallback(MessageReceivedEventArgs eventArgs) { + using var loggerScope = _logger.BeginScope(new Messaging.Common.LoggingDataDictionary { + { "ThreadId", Environment.CurrentManagedThreadId }, + }); + if (!_storageInfoProvider.HasSpaceAvailableForExport) { _logger.ExportServiceStoppedDueToLowStorageSpace(_storageInfoProvider.AvailableFreeSpace); - _messageSubscriber.Reject(eventArgs.Message); + MessageSubscriber.Reject(eventArgs.Message); + return; + } + + if (Interlocked.Read(ref _activeWorkers) >= Concurrency) + { + _logger.ExceededMaxmimumNumberOfWorkers(ServiceName, _activeWorkers); + await Task.Delay(200).ConfigureAwait(false); // small delay to stop instantly dead lettering the next message. + MessageSubscriber.Reject(eventArgs.Message); return; } + Interlocked.Increment(ref _activeWorkers); try { - var executionOptions = new ExecutionDataflowBlockOptions + await ProcessMessage(eventArgs).ConfigureAwait(false); + } + catch (AggregateException ex) + { + foreach (var iex in ex.InnerExceptions) { - MaxDegreeOfParallelism = Concurrency, - MaxMessagesPerTask = 1, - CancellationToken = _cancellationTokenSource.Token - }; + _logger.ErrorExporting(iex); + } + } + catch (Exception ex) + { + _logger.ErrorProcessingExportTask(ex); + } + finally + { + Interlocked.Decrement(ref _activeWorkers); + } + } - var exportFlow = new TransformManyBlock( - exportRequest => DownloadPayloadActionCallback(exportRequest, _cancellationTokenSource.Token), - executionOptions); + TransformBlock GetoutputDataEngineBlock(ExecutionDataflowBlockOptions executionOptions) + { + return new TransformBlock( + async (exportDataRequest) => + { + try + { + if (exportDataRequest.IsFailed) return exportDataRequest; + return await ExecuteOutputDataEngineCallback(exportDataRequest).ConfigureAwait(false); + } + catch (Exception e) + { + exportDataRequest.SetFailed(FileExportStatus.ServiceError, $"failed to execute plugin {e.Message}"); + return exportDataRequest; + } + }, + executionOptions); + } - var exportActionBlock = new TransformBlock( - async (exportDataRequest) => + TransformBlock GetxportActionBlock(ExecutionDataflowBlockOptions executionOptions) + { + return new TransformBlock( + async (exportDataRequest) => + { + try { if (exportDataRequest.IsFailed) return exportDataRequest; return await ExportDataBlockCallback(exportDataRequest, _cancellationTokenSource.Token).ConfigureAwait(false); - }, - executionOptions); + } + catch (Exception e) + { - var reportingActionBlock = new ActionBlock(ReportingActionBlock, executionOptions); + exportDataRequest.SetFailed(FileExportStatus.ServiceError, $"Failed during export {e.Message}"); + return exportDataRequest; + } - var linkOptions = new DataflowLinkOptions { PropagateCompletion = true }; + }, + executionOptions); + } - exportFlow.LinkTo(exportActionBlock, linkOptions); - exportActionBlock.LinkTo(reportingActionBlock, linkOptions); + protected (TransformManyBlock, ActionBlock) SetupActionBlocks() + { + var executionOptions = new ExecutionDataflowBlockOptions + { + MaxDegreeOfParallelism = Concurrency, + MaxMessagesPerTask = 1, + CancellationToken = _cancellationTokenSource.Token + }; - lock (SyncRoot) - { - var exportRequest = eventArgs.Message.ConvertTo(); - if (_exportRequests.ContainsKey(exportRequest.ExportTaskId)) - { - _logger.ExportRequestAlreadyQueued(exportRequest.CorrelationId, exportRequest.ExportTaskId); - return; - } + var exportFlow = new TransformManyBlock( + exportRequest => DownloadPayloadActionCallback(exportRequest, _cancellationTokenSource.Token), + executionOptions); - exportRequest.MessageId = eventArgs.Message.MessageId; - exportRequest.DeliveryTag = eventArgs.Message.DeliveryTag; + var outputDataEngineBLock = GetoutputDataEngineBlock(executionOptions); - var exportRequestWithDetails = new ExportRequestEventDetails(exportRequest); + var exportActionBlock = GetxportActionBlock(executionOptions); - _exportRequests.Add(exportRequest.ExportTaskId, exportRequestWithDetails); - exportFlow.Post(exportRequestWithDetails); - _logger.ExportRequestQueuedForProcessing(exportRequest.CorrelationId, exportRequest.ExportTaskId); - } + var reportingActionBlock = new ActionBlock(ReportingActionBlock, executionOptions); - exportFlow.Complete(); - reportingActionBlock.Completion.Wait(_cancellationTokenSource.Token); - } - catch (AggregateException ex) + var linkOptions = new DataflowLinkOptions { PropagateCompletion = true }; + + exportFlow.LinkTo(outputDataEngineBLock, linkOptions); + outputDataEngineBLock.LinkTo(exportActionBlock, linkOptions); + exportActionBlock.LinkTo(reportingActionBlock, linkOptions); + + return (exportFlow, reportingActionBlock); + } + + protected void HandleCStoreException(Exception ex, ExportRequestDataMessage exportRequestData) + { + var exception = ex; + var fillStatus = FileExportStatus.ServiceError; + + if (exception is AggregateException) { - foreach (var iex in ex.InnerExceptions) - { - _logger.ErrorExporting(iex); - } + exception = exception.InnerException!; } - catch (Exception ex) + + var errorMessage = $"Job failed with error: {exception.Message}."; + + switch (exception) { - _logger.ErrorProcessingExportTask(ex); + case DicomAssociationAbortedException abortEx: + errorMessage = $"Association aborted with reason {abortEx.AbortReason}."; + break; + case DicomAssociationRejectedException rejectEx: + errorMessage = $"Association rejected with reason {rejectEx.RejectReason}."; + break; + case SocketException socketException: + errorMessage = $"Association aborted with error {socketException.Message}."; + break; + case ConfigurationException configException: + errorMessage = $"{configException.Message}"; + fillStatus = FileExportStatus.ConfigurationError; + break; + case ExternalAppExeception appException: + errorMessage = $"{appException.Message}"; + break; } + + _logger.ExportException(errorMessage, ex); + exportRequestData.SetFailed(fillStatus, errorMessage); } // TPL doesn't yet support IAsyncEnumerable // https://github.com/dotnet/runtime/issues/30863 private IEnumerable DownloadPayloadActionCallback(ExportRequestEventDetails exportRequest, CancellationToken cancellationToken) { - Guard.Against.Null(exportRequest); + Guard.Against.Null(exportRequest, nameof(exportRequest)); using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "ExportTaskId", exportRequest.ExportTaskId }, { "CorrelationId", exportRequest.CorrelationId } }); - var scope = _serviceScopeFactory.CreateScope(); + var scope = ServiceScopeFactory.CreateScope(); var storageService = scope.ServiceProvider.GetRequiredService(); foreach (var file in exportRequest.Files) @@ -225,12 +319,13 @@ private IEnumerable DownloadPayloadActionCallback(Expo .ExecuteAsync(async () => { _logger.DownloadingFile(file); - var stream = await storageService.GetObjectAsync(_configuration.Storage.StorageServiceBucketName, file, cancellationToken).ConfigureAwait(false) as MemoryStream; + var stream = (await storageService.GetObjectAsync(_configuration.Storage.StorageServiceBucketName, file, cancellationToken).ConfigureAwait(false) as MemoryStream)!; exportRequestData.SetData(stream.ToArray()); _logger.FileReadyForExport(file); + ExportCompleteCallback(exportRequestData).GetAwaiter().GetResult(); }); - task.Wait(); + task.Wait(cancellationToken); } catch (Exception ex) { @@ -243,11 +338,20 @@ private IEnumerable DownloadPayloadActionCallback(Expo } } - private void ReportingActionBlock(ExportRequestDataMessage exportRequestData) + protected virtual async Task ExecuteOutputDataEngineCallback(ExportRequestDataMessage exportDataRequest) { - using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId } }); + using var loggerScope = _logger.BeginScope(new Messaging.Common.LoggingDataDictionary { + { "WorkflowInstanceId", exportDataRequest.WorkflowInstanceId }, + { "TaskId", exportDataRequest.ExportTaskId } + }); + var outputDataEngine = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IOutputDataPlugInEngine)); + + outputDataEngine.Configure(exportDataRequest.PlugInAssemblies); + return await outputDataEngine.ExecutePlugInsAsync(exportDataRequest).ConfigureAwait(false); + } - var exportRequest = _exportRequests[exportRequestData.ExportTaskId]; + private static void HandleStatus(ExportRequestDataMessage exportRequestData, ExportRequestEventDetails exportRequest) + { lock (SyncRoot) { exportRequest.FileStatuses.Add(exportRequestData.Filename, exportRequestData.ExportStatus); @@ -264,31 +368,57 @@ private void ReportingActionBlock(ExportRequestDataMessage exportRequestData) { exportRequest.AddErrorMessages(exportRequestData.Messages); } + } + } - if (!exportRequest.IsCompleted) - { - return; - } + private void ReportingActionBlock(ExportRequestDataMessage exportRequestData) + { + var exportRequest = ExportRequests[exportRequestData.ExportTaskId]; + HandleStatus(exportRequestData, exportRequest); + if (!exportRequest.IsCompleted) + { + return; } - _logger.ExportCompleted(exportRequest.FailedFiles, exportRequest.Files.Count()); + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId } }); + _logger.ExportCompleted(exportRequest.FailedFiles, exportRequest.Files.Count(), exportRequest.Duration.TotalMilliseconds); var exportCompleteEvent = new ExportCompleteEvent(exportRequest, exportRequest.Status, exportRequest.FileStatuses); var jsonMessage = new JsonMessage(exportCompleteEvent, MessageBrokerConfiguration.InformaticsGatewayApplicationId, exportRequest.CorrelationId, exportRequest.DeliveryTag); + FinaliseMessage(jsonMessage); + + lock (SyncRoot) + { + ExportRequests.Remove(exportRequestData.ExportTaskId); + } + + if (ReportActionCompleted != null) + { + _logger.CallingReportActionCompletedCallback(); + ReportActionCompleted(this, EventArgs.Empty); + } + } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + protected virtual async Task ExportCompleteCallback(ExportRequestDataMessage exportRequestData) { } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + + private void FinaliseMessage(JsonMessage jsonMessage) + { Policy .Handle() .WaitAndRetry( _configuration.Export.Retries.RetryDelays, (exception, timeSpan, retryCount, context) => { - _logger.ErrorAcknowledgingMessageWithRetry(exception, timeSpan, retryCount); + _logger.ErrorPublishingExportCompleteEventWithRetry(exception, timeSpan, retryCount); }) .Execute(() => { - _logger.SendingAcknowledgement(); - _messageSubscriber.Acknowledge(jsonMessage); + _logger.PublishingExportCompleteEvent(); + MessagePublisher.Publish(ExportCompleteTopic, jsonMessage.ToMessage()); }); Policy @@ -297,37 +427,193 @@ private void ReportingActionBlock(ExportRequestDataMessage exportRequestData) _configuration.Export.Retries.RetryDelays, (exception, timeSpan, retryCount, context) => { - _logger.ErrorPublishingExportCompleteEventWithRetry(exception, timeSpan, retryCount); + _logger.ErrorAcknowledgingMessageWithRetry(exception, timeSpan, retryCount); }) .Execute(() => { - _logger.PublishingExportCompleteEvent(); - _messagePublisher.Publish(_configuration.Messaging.Topics.ExportComplete, jsonMessage.ToMessage()); + _logger.SendingAcknowledgement(); + MessageSubscriber.Acknowledge(jsonMessage); }); + } - lock (SyncRoot) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - _exportRequests.Remove(exportRequestData.ExportTaskId); + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; } + } - if (ReportActionCompleted != null) + private async Task LookupDestinationAsync(string destinationName, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(destinationName)) { - _logger.CallingReportActionCompletedCallback(); - ReportActionCompleted(this, EventArgs.Empty); + throw new ConfigurationException("Export task does not have destination set."); } + + using var scope = ServiceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + var destination = await repository.FindByNameAsync(destinationName, cancellationToken).ConfigureAwait(false); + + return destination is null + ? throw new ConfigurationException($"Specified destination '{destinationName}' does not exist.") + : destination; } - protected virtual void Dispose(bool disposing) + protected virtual async Task GetDestination(ExportRequestDataMessage exportRequestData, string destinationName, CancellationToken cancellationToken) { - if (!_disposedValue) + try { - if (disposing) + return await LookupDestinationAsync(destinationName, cancellationToken).ConfigureAwait(false); + } + catch (ConfigurationException ex) + { + HandleCStoreException(ex, exportRequestData); + return null; + } + } + + protected virtual async Task HandleDesination(ExportRequestDataMessage exportRequestData, string destinationName, CancellationToken cancellationToken) + { + Guard.Against.Null(exportRequestData, nameof(exportRequestData)); + + var manualResetEvent = new ManualResetEvent(false); + var destination = await GetDestination(exportRequestData, destinationName, cancellationToken).ConfigureAwait(false); + if (destination is null) + { + return; + } + + try + { + await ExecuteExport(exportRequestData, manualResetEvent, destination!, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + HandleCStoreException(ex, exportRequestData); + } + } + + private async Task GenerateRequestsAsync( + ExportRequestDataMessage exportRequestData, + IDicomClient client, + ManualResetEvent manualResetEvent) + { + DicomFile dicomFile; + try + { + dicomFile = _dicomToolkit.Load(exportRequestData.FileContent); + } + catch (Exception ex) + { + var errorMessage = $"Error reading DICOM file: {ex.Message}"; + _logger.ExportException(errorMessage, ex); + exportRequestData.SetFailed(FileExportStatus.UnsupportedDataType, errorMessage); + return false; + } + + try + { + var request = new DicomCStoreRequest(dicomFile); + + request.OnResponseReceived += (req, response) => { - _scope.Dispose(); + if (response.Status == DicomStatus.Success) + { + _logger.DimseExportInstanceComplete(); + } + else + { + var errorMessage = $"Failed to export with error {response.Status}"; + _logger.DimseExportInstanceError(response.Status); + exportRequestData.SetFailed(FileExportStatus.ServiceError, errorMessage); + } + manualResetEvent.Set(); + }; + + await client.AddRequestAsync(request).ConfigureAwait(false); + return true; + } + catch (Exception exception) + { + var errorMessage = $"Error while adding DICOM C-STORE request: {exception.Message}"; + _logger.DimseExportErrorAddingInstance(exception.Message, exception); + exportRequestData.SetFailed(FileExportStatus.ServiceError, errorMessage); + return false; + } + } + + protected async Task ExecuteExport(ExportRequestDataMessage exportRequestData, ManualResetEvent manualResetEvent, DestinationApplicationEntity destination, CancellationToken cancellationToken) => await Policy + .Handle() + .WaitAndRetryAsync( + _configuration.Export.Retries.RetryDelays, + (exception, timeSpan, retryCount, context) => + { + _logger.DimseExportErrorWithRetry(timeSpan, retryCount, exception); + }) + .ExecuteAsync(async () => + { + var client = DicomClientFactory.Create( + destination.HostIp, + destination.Port, + false, + _configuration.Dicom.Scu.AeTitle, + destination.AeTitle); + + client.AssociationAccepted += (sender, args) => _logger.ExportAssociationAccepted(); + client.AssociationRejected += (sender, args) => _logger.ExportAssociationRejected(); + client.AssociationReleased += (sender, args) => _logger.ExportAssociationReleased(); + client.ServiceOptions.LogDataPDUs = _configuration.Dicom.Scu.LogDataPdus; + client.ServiceOptions.LogDimseDatasets = _configuration.Dicom.Scu.LogDimseDatasets; + + client.NegotiateAsyncOps(); + if (await GenerateRequestsAsync(exportRequestData, client, manualResetEvent).ConfigureAwait(false)) + { + _logger.DimseExporting(destination.AeTitle, destination.HostIp, destination.Port); + await client.SendAsync(cancellationToken).ConfigureAwait(false); + manualResetEvent.WaitOne(); + _logger.DimseExportComplete(destination.AeTitle); + } + }).ConfigureAwait(false); + + protected async Task BaseProcessMessage(MessageReceivedEventArgs eventArgs) + { + var (exportFlow, reportingActionBlock) = SetupActionBlocks(); + + lock (SyncRoot) + { + var exportRequest = eventArgs.Message.ConvertTo(); + if (ExportRequests.ContainsKey(exportRequest.ExportTaskId)) + { + _logger.ExportRequestAlreadyQueued(exportRequest.CorrelationId, exportRequest.ExportTaskId); + return; } - _disposedValue = true; + exportRequest.MessageId = eventArgs.Message.MessageId; + exportRequest.DeliveryTag = eventArgs.Message.DeliveryTag; + + var exportRequestWithDetails = new ExportRequestEventDetails(exportRequest); + + ExportRequests.Add(exportRequest.ExportTaskId, exportRequestWithDetails); + if (!exportFlow.Post(exportRequestWithDetails)) + { + _logger.ErrorPostingExportJobToQueue(exportRequest.CorrelationId, exportRequest.ExportTaskId); + MessageSubscriber.Reject(eventArgs.Message); + } + else + { + _logger.ExportRequestQueuedForProcessing(exportRequest.CorrelationId, exportRequest.MessageId, exportRequest.ExportTaskId); + } } + + exportFlow.Complete(); + await reportingActionBlock.Completion.ConfigureAwait(false); + } public void Dispose() diff --git a/src/InformaticsGateway/Services/Export/ExtAppScuExportService.cs b/src/InformaticsGateway/Services/Export/ExtAppScuExportService.cs new file mode 100755 index 000000000..fb602be5d --- /dev/null +++ b/src/InformaticsGateway/Services/Export/ExtAppScuExportService.cs @@ -0,0 +1,166 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.Messaging.Common; +using Monai.Deploy.Messaging.Events; + +namespace Monai.Deploy.InformaticsGateway.Services.Export +{ + public class ExtAppScuExportService : ExportServiceBase + { + private readonly ILogger _logger; + private readonly IOptions _configuration; + private readonly IExternalAppDetailsRepository _repository; + private readonly IDicomToolkit _dicomToolkit; + protected override ushort Concurrency { get; } + public override string RoutingKey { get; } + public override string ServiceName => "External App Export Service"; + + public ExtAppScuExportService( + ILogger logger, + IServiceScopeFactory serviceScopeFactory, + IOptions configuration, + IDicomToolkit dicomToolkit, + IExternalAppDetailsRepository repository) + : base(logger, configuration, serviceScopeFactory, dicomToolkit) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); + RoutingKey = $"{configuration.Value.Messaging.Topics.ExternalAppRequest}"; + Concurrency = _configuration.Value.Dicom.Scu.MaximumNumberOfAssociations; + } + + protected override async Task ProcessMessage(MessageReceivedEventArgs eventArgs) + { + var (exportFlow, reportingActionBlock) = SetupActionBlocks(); + + lock (SyncRoot) + { + var externalAppRequest = eventArgs.Message.ConvertTo(); + if (ExportRequests.ContainsKey(externalAppRequest.ExportTaskId)) + { + _logger.ExportRequestAlreadyQueued(externalAppRequest.CorrelationId, externalAppRequest.ExportTaskId); + return; + } + + externalAppRequest.MessageId = eventArgs.Message.MessageId; + externalAppRequest.DeliveryTag = eventArgs.Message.DeliveryTag; + + var exportRequestWithDetails = new ExportRequestEventDetails(externalAppRequest); + + ExportRequests.Add(externalAppRequest.ExportTaskId, exportRequestWithDetails); + if (!exportFlow.Post(exportRequestWithDetails)) + { + _logger.ErrorPostingExportJobToQueue(externalAppRequest.CorrelationId, externalAppRequest.ExportTaskId); + MessageSubscriber.Reject(eventArgs.Message); + } + else + { + _logger.ExportRequestQueuedForProcessing(externalAppRequest.CorrelationId, externalAppRequest.MessageId, externalAppRequest.ExportTaskId); + } + } + + exportFlow.Complete(); + await reportingActionBlock.Completion.ConfigureAwait(false); + } + + protected override async Task ExportCompleteCallback(ExportRequestDataMessage exportRequestData) + { + try + { + var dicom = _dicomToolkit.Load(exportRequestData.FileContent); + dicom.Dataset.TryGetString(DicomTag.PatientID, out var patientID); + if (dicom.Dataset.TryGetString(DicomTag.StudyInstanceUID, out var studyInstUID)) + { + var (newStudyInstanceUID, newPatientId) = + await SaveInRepo(exportRequestData, studyInstUID, patientID ?? string.Empty) + .ConfigureAwait(false); + dicom.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, newStudyInstanceUID); + dicom.Dataset.AddOrUpdate(DicomTag.PatientID, newPatientId); + + using var ms = new MemoryStream(); + await dicom.SaveAsync(ms).ConfigureAwait(false); + exportRequestData.SetData(ms.ToArray()); + return; + } + throw new ExternalAppExeception("No StudyInstanceUID tag found"); + } + catch (Exception ex) + { + var errorMessage = $"Error reading DICOM file: {ex.Message}"; + _logger.ExportException(errorMessage, ex); + exportRequestData.SetFailed(FileExportStatus.UnsupportedDataType, errorMessage); + } + + } + + private async Task<(string, string)> SaveInRepo(ExportRequestDataMessage externalAppRequest, string studyinstanceId, string patientId) + { + var existing = (await _repository.GetAsync(studyinstanceId, new CancellationToken()).ConfigureAwait(false)) + ?.Find(e => e.WorkflowInstanceId == externalAppRequest.WorkflowInstanceId && + e.ExportTaskID == externalAppRequest.ExportTaskId); + if (existing is null) + { + var studyInstanceUidOutBound = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var PatientIdOutbound = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + await _repository.AddAsync(new ExternalAppDetails + { + StudyInstanceUid = studyinstanceId, + StudyInstanceUidOutBound = studyInstanceUidOutBound, + WorkflowInstanceId = externalAppRequest.WorkflowInstanceId, + ExportTaskID = externalAppRequest.ExportTaskId, + CorrelationId = externalAppRequest.CorrelationId, + DateTimeCreated = DateTime.Now, + DestinationFolder = externalAppRequest.FilePayloadId, + PatientId = patientId, + PatientIdOutBound = PatientIdOutbound + }, new CancellationToken()).ConfigureAwait(false); + _logger.SavingExternalAppData(studyinstanceId); + return (studyInstanceUidOutBound, PatientIdOutbound); + } + return (existing.StudyInstanceUidOutBound, existing.PatientIdOutBound); + } + + protected override async Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken) + { + using var loggerScope = _logger.BeginScope(new Messaging.Common.LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId }, { "Filename", exportRequestData.Filename } }); + + foreach (var destinationName in exportRequestData.Destinations) + { + await HandleDesination(exportRequestData, destinationName, cancellationToken).ConfigureAwait(false); + } + + return exportRequestData; + } + } +} diff --git a/src/InformaticsGateway/Services/Export/ExternalAppExeception.cs b/src/InformaticsGateway/Services/Export/ExternalAppExeception.cs new file mode 100755 index 000000000..61af23027 --- /dev/null +++ b/src/InformaticsGateway/Services/Export/ExternalAppExeception.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Services.Export +{ + public class ExternalAppExeception : Exception + { + public ExternalAppExeception() + { + } + + public ExternalAppExeception(string message) : base(message) + { + } + + public ExternalAppExeception(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Services/Export/Hl7ExportService.cs b/src/InformaticsGateway/Services/Export/Hl7ExportService.cs new file mode 100755 index 000000000..dafb83e2e --- /dev/null +++ b/src/InformaticsGateway/Services/Export/Hl7ExportService.cs @@ -0,0 +1,164 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.Messaging.Common; +using Polly; +using System.Linq; + +namespace Monai.Deploy.InformaticsGateway.Services.Export +{ + internal class Hl7ExportService : ExportServiceBase + { + private readonly ILogger _logger; + private readonly InformaticsGatewayConfiguration _configuration; + + protected override ushort Concurrency { get; } + public override string RoutingKey { get; } + public override string ServiceName => "DICOM Export HL7 Service"; + private readonly IMllpService _mllpService; + + + public Hl7ExportService( + ILogger logger, + IServiceScopeFactory serviceScopeFactory, + IOptions configuration, + IDicomToolkit dicomToolkit, + IMllpService mllpService) + : base(logger, configuration, serviceScopeFactory, dicomToolkit) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _configuration = configuration.Value ?? throw new ArgumentNullException(nameof(configuration)); + + RoutingKey = $"{configuration.Value.Messaging.Topics.ExportHL7}"; + ExportCompleteTopic = $"{configuration.Value.Messaging.Topics.ExportHl7Complete}"; + Concurrency = _configuration.Dicom.Scu.MaximumNumberOfAssociations; + _mllpService = mllpService ?? throw new ArgumentNullException(nameof(mllpService)); + } + + + protected override Task ProcessMessage(MessageReceivedEventArgs eventArgs) + { + return BaseProcessMessage(eventArgs); + } + + + protected override async Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken) + { + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary + { + { "ExportTaskId", exportRequestData.ExportTaskId }, + { "CorrelationId", exportRequestData.CorrelationId }, + { "Filename", exportRequestData.Filename } + }); + + foreach (var destinationName in exportRequestData.Destinations) + { + await HandleDesination(exportRequestData, destinationName, cancellationToken).ConfigureAwait(false); + } + + return exportRequestData; + } + + protected override async Task HandleDesination(ExportRequestDataMessage exportRequestData, string destinationName, CancellationToken cancellationToken) + { + Guard.Against.Null(exportRequestData, nameof(exportRequestData)); + + var destination = await GetHL7Destination(exportRequestData, destinationName, cancellationToken).ConfigureAwait(false); + if (destination is null) + { + return; + } + + try + { + await ExecuteHl7Export(exportRequestData, destination!, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + HandleCStoreException(ex, exportRequestData); + } + } + + private async Task ExecuteHl7Export( + ExportRequestDataMessage exportRequestData, + HL7DestinationEntity destination, + CancellationToken cancellationToken) => await Policy + .Handle() + .WaitAndRetryAsync( + _configuration.Export.Retries.RetryDelays, + (exception, timeSpan, retryCount, context) => + { + _logger.HL7ExportErrorWithRetry(timeSpan, retryCount, exception); + }) + .ExecuteAsync(async () => + { + await _mllpService.SendMllp( + Dns.GetHostAddresses(destination.HostIp)[0], + destination.Port, Encoding.UTF8.GetString(exportRequestData.FileContent), + cancellationToken + ).ConfigureAwait(false); + }).ConfigureAwait(false); + + + private async Task LookupDestinationAsync(string destinationName, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(destinationName)) + { + throw new ConfigurationException("Export task does not have destination set."); + } + + using var scope = ServiceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + var destination = await repository.FindByNameAsync(destinationName, cancellationToken).ConfigureAwait(false); + + return destination is null + ? throw new ConfigurationException($"Specified destination '{destinationName}' does not exist.") + : destination; + } + + private async Task GetHL7Destination(ExportRequestDataMessage exportRequestData, string destinationName, CancellationToken cancellationToken) + { + try + { + return await LookupDestinationAsync(destinationName, cancellationToken).ConfigureAwait(false); + } + catch (ConfigurationException ex) + { + HandleCStoreException(ex, exportRequestData); + return null; + } + } + + protected override Task ExecuteOutputDataEngineCallback(ExportRequestDataMessage exportDataRequest) + { + return Task.FromResult(exportDataRequest); + } + } +} diff --git a/src/InformaticsGateway/Services/Export/ScuExportService.cs b/src/InformaticsGateway/Services/Export/ScuExportService.cs old mode 100644 new mode 100755 index e1bdd4ac7..d87e73e6a --- a/src/InformaticsGateway/Services/Export/ScuExportService.cs +++ b/src/InformaticsGateway/Services/Export/ScuExportService.cs @@ -16,34 +16,24 @@ */ using System; -using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -using Ardalis.GuardClauses; -using FellowOakDicom; -using FellowOakDicom.Network; -using FellowOakDicom.Network.Client; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; -using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; -using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.Messaging.Events; -using Polly; +using Monai.Deploy.Messaging.Common; namespace Monai.Deploy.InformaticsGateway.Services.Export { internal class ScuExportService : ExportServiceBase { private readonly ILogger _logger; - private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IOptions _configuration; - private readonly IDicomToolkit _dicomToolkit; - protected override int Concurrency { get; } + protected override ushort Concurrency { get; } public override string RoutingKey { get; } public override string ServiceName => "DICOM Export Service"; @@ -52,181 +42,30 @@ public ScuExportService( IServiceScopeFactory serviceScopeFactory, IOptions configuration, IDicomToolkit dicomToolkit) - : base(logger, configuration, serviceScopeFactory) + : base(logger, configuration, serviceScopeFactory, dicomToolkit) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - _dicomToolkit = dicomToolkit ?? throw new ArgumentNullException(nameof(dicomToolkit)); RoutingKey = $"{configuration.Value.Messaging.Topics.ExportRequestPrefix}.{_configuration.Value.Dicom.Scu.AgentName}"; Concurrency = _configuration.Value.Dicom.Scu.MaximumNumberOfAssociations; } - protected override async Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken) + protected override async Task ProcessMessage(MessageReceivedEventArgs eventArgs) { - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId }, { "Filename", exportRequestData.Filename } }); - - foreach (var destinationName in exportRequestData.Destinations) - { - await HandleDesination(exportRequestData, destinationName, cancellationToken); - } - - return exportRequestData; + await BaseProcessMessage(eventArgs); } - private async Task HandleDesination(ExportRequestDataMessage exportRequestData, string destinationName, CancellationToken cancellationToken) - { - Guard.Against.Null(exportRequestData); - - var manualResetEvent = new ManualResetEvent(false); - DestinationApplicationEntity destination = null; - try - { - destination = await LookupDestinationAsync(destinationName, cancellationToken).ConfigureAwait(false); - } - catch (ConfigurationException ex) - { - _logger.ScuExportConfigurationError(ex.Message, ex); - exportRequestData.SetFailed(FileExportStatus.ConfigurationError, ex.Message); - return; - } - - try - { - await Policy - .Handle() - .WaitAndRetryAsync( - _configuration.Value.Export.Retries.RetryDelays, - (exception, timeSpan, retryCount, context) => - { - _logger.DimseExportErrorWithRetry(timeSpan, retryCount, exception); - }) - .ExecuteAsync(async () => - { - var client = DicomClientFactory.Create( - destination.HostIp, - destination.Port, - false, - _configuration.Value.Dicom.Scu.AeTitle, - destination.AeTitle); - - client.AssociationAccepted += (sender, args) => _logger.ExportAssociationAccepted(); - client.AssociationRejected += (sender, args) => _logger.ExportAssociationRejected(); - client.AssociationReleased += (sender, args) => _logger.ExportAssociationReleased(); - client.ServiceOptions.LogDataPDUs = _configuration.Value.Dicom.Scu.LogDataPdus; - client.ServiceOptions.LogDimseDatasets = _configuration.Value.Dicom.Scu.LogDimseDatasets; - - client.NegotiateAsyncOps(); - if (await GenerateRequestsAsync(exportRequestData, client, manualResetEvent)) - { - _logger.DimseExporting(destination.AeTitle, destination.HostIp, destination.Port); - await client.SendAsync(cancellationToken).ConfigureAwait(false); - manualResetEvent.WaitOne(); - _logger.DimseExportComplete(destination.AeTitle); - } - }).ConfigureAwait(false); - } - catch (Exception ex) - { - HandleCStoreException(ex, exportRequestData); - } - } - - private async Task LookupDestinationAsync(string destinationName, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(destinationName)) - { - throw new ConfigurationException("Export task does not have destination set."); - } - - using var scope = _serviceScopeFactory.CreateScope(); - var repository = scope.ServiceProvider.GetRequiredService(); - var destination = await repository.FindByNameAsync(destinationName, cancellationToken).ConfigureAwait(false); - - if (destination is null) - { - throw new ConfigurationException($"Specified destination '{destinationName}' does not exist."); - } - - return destination; - } - - private async Task GenerateRequestsAsync( - ExportRequestDataMessage exportRequestData, - IDicomClient client, - ManualResetEvent manualResetEvent) - { - DicomFile dicomFile; - try - { - dicomFile = _dicomToolkit.Load(exportRequestData.FileContent); - } - catch (Exception ex) - { - var errorMessage = $"Error reading DICOM file: {ex.Message}"; - _logger.ExportException(errorMessage, ex); - exportRequestData.SetFailed(FileExportStatus.UnsupportedDataType, errorMessage); - return false; - } - - try - { - var request = new DicomCStoreRequest(dicomFile); - - request.OnResponseReceived += (req, response) => - { - if (response.Status == DicomStatus.Success) - { - _logger.DimseExportInstanceComplete(); - } - else - { - var errorMessage = $"Failed to export with error {response.Status}"; - _logger.DimseExportInstanceError(response.Status); - exportRequestData.SetFailed(FileExportStatus.ServiceError, errorMessage); - } - manualResetEvent.Set(); - }; - - await client.AddRequestAsync(request).ConfigureAwait(false); - return true; - } - catch (Exception exception) - { - var errorMessage = $"Error while adding DICOM C-STORE request: {exception.Message}"; - _logger.DimseExportErrorAddingInstance(exception.Message, exception); - exportRequestData.SetFailed(FileExportStatus.ServiceError, errorMessage); - return false; - } - } - - private void HandleCStoreException(Exception ex, ExportRequestDataMessage exportRequestData) + protected override async Task ExportDataBlockCallback(ExportRequestDataMessage exportRequestData, CancellationToken cancellationToken) { - var exception = ex; - - if (exception is AggregateException) - { - exception = exception.InnerException; - } + using var loggerScope = _logger.BeginScope(new Api.LoggingDataDictionary { { "ExportTaskId", exportRequestData.ExportTaskId }, { "CorrelationId", exportRequestData.CorrelationId }, { "Filename", exportRequestData.Filename } }); - var errorMessage = $"Job failed with error: {exception.Message}."; - - if (exception is DicomAssociationAbortedException abortEx) - { - errorMessage = $"Association aborted with reason {abortEx.AbortReason}."; - } - else if (exception is DicomAssociationRejectedException rejectEx) - { - errorMessage = $"Association rejected with reason {rejectEx.RejectReason}."; - } - else if (exception is SocketException socketException) + foreach (var destinationName in exportRequestData.Destinations) { - errorMessage = $"Association aborted with error {socketException.Message}."; + await HandleDesination(exportRequestData, destinationName, cancellationToken).ConfigureAwait(false); } - _logger.ExportException(errorMessage, ex); - exportRequestData.SetFailed(FileExportStatus.ServiceError, errorMessage); + return exportRequestData; } } } diff --git a/src/InformaticsGateway/Services/Fhir/FhirJsonReader.cs b/src/InformaticsGateway/Services/Fhir/FhirJsonReader.cs index 1f976b8a8..cd48394fb 100644 --- a/src/InformaticsGateway/Services/Fhir/FhirJsonReader.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirJsonReader.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.Fhir { @@ -48,11 +49,11 @@ public FhirJsonReader(ILogger logger, IOptions GetContentAsync(HttpRequest request, string correlationId, string resourceType, MediaTypeHeaderValue mediaTypeHeaderValue, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.Null(request, nameof(request)); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); Guard.Against.NullOrInvalidInput(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue), (value) => { - return value.MediaType.Value.Equals(ContentTypes.ApplicationFhirJson, StringComparison.OrdinalIgnoreCase); + return value.MediaType.Value!.Equals(ContentTypes.ApplicationFhirJson, StringComparison.OrdinalIgnoreCase); }); _logger.ParsingFhirJson(); @@ -63,18 +64,18 @@ public async Task GetContentAsync(HttpRequest request, string c RawData = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false) }; - var jsonDoc = JsonNode.Parse(result.RawData); + var jsonDoc = JsonNode.Parse(result.RawData)!; if (jsonDoc[Resources.PropertyResourceType] is not null) { - result.InternalResourceType = jsonDoc[Resources.PropertyResourceType].GetValue(); + result.InternalResourceType = jsonDoc[Resources.PropertyResourceType]?.GetValue() ?? string.Empty; } var resourceId = SetIdIfMIssing(correlationId, jsonDoc); result.RawData = jsonDoc.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); - var fileMetadata = new FhirFileStorageMetadata(correlationId, result.InternalResourceType, resourceId, Api.Rest.FhirStorageFormat.Json); + var fileMetadata = new FhirFileStorageMetadata(correlationId, result.InternalResourceType, resourceId, Api.Rest.FhirStorageFormat.Json, DataService.FHIR, request.HttpContext.Connection.RemoteIpAddress!.ToString()); await fileMetadata.SetDataStream(result.RawData, _options.Value.Storage.TemporaryDataStorage, _fileSystem, _options.Value.Storage.LocalTemporaryStoragePath); result.Metadata = fileMetadata; @@ -83,15 +84,15 @@ public async Task GetContentAsync(HttpRequest request, string c private static string SetIdIfMIssing(string correlationId, JsonNode jsonDoc) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.Null(jsonDoc); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.Null(jsonDoc, nameof(jsonDoc)); if (string.IsNullOrWhiteSpace(jsonDoc[Resources.PropertyId]?.GetValue())) { jsonDoc[Resources.PropertyId] = correlationId; } - return jsonDoc[Resources.PropertyId].GetValue(); + return jsonDoc[Resources.PropertyId]?.GetValue() ?? string.Empty; } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Fhir/FhirResourceTypesRouteConstraint.cs b/src/InformaticsGateway/Services/Fhir/FhirResourceTypesRouteConstraint.cs old mode 100644 new mode 100755 index 1c5d30d5b..0353a8afa --- a/src/InformaticsGateway/Services/Fhir/FhirResourceTypesRouteConstraint.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirResourceTypesRouteConstraint.cs @@ -22,12 +22,12 @@ namespace Monai.Deploy.InformaticsGateway.Services.Fhir { internal class FhirResourceTypesRouteConstraint : IRouteConstraint { - public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) + public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { - Guard.Against.Null(httpContext); - Guard.Against.Null(route); - Guard.Against.NullOrWhiteSpace(routeKey); - Guard.Against.Null(values); + Guard.Against.Null(httpContext, nameof(httpContext)); + Guard.Against.Null(route, nameof(route)); + Guard.Against.NullOrWhiteSpace(routeKey, nameof(routeKey)); + Guard.Against.Null(values, nameof(values)); return (values.TryGetValue(Resources.RouteNameResourceType, out var resourceTypeObject) && resourceTypeObject is string resourceType && diff --git a/src/InformaticsGateway/Services/Fhir/FhirService.cs b/src/InformaticsGateway/Services/Fhir/FhirService.cs index f1cc9eab6..0cd2830ca 100644 --- a/src/InformaticsGateway/Services/Fhir/FhirService.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,13 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; +using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.Fhir { @@ -58,8 +60,8 @@ public FhirService(IServiceScopeFactory serviceScopeFactory, IOptions StoreAsync(HttpRequest request, string correlationId, string resourceType, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.Null(request, nameof(request)); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); if (!MediaTypeHeaderValue.TryParse(request.ContentType, out var mediaTypeHeaderValue)) { @@ -87,8 +89,9 @@ public async Task StoreAsync(HttpRequest request, string correl throw new FhirStoreException(correlationId, $"Provided resource is of type '{content.InternalResourceType}' but request targeted type '{resourceType}'.", IssueType.Invalid); } + var payloadId = await _payloadAssembler.Queue(correlationId, content.Metadata!, new DataOrigin { DataService = DataService.FHIR, Source = request.HttpContext.Connection.RemoteIpAddress!.ToString(), Destination = FileStorageMetadata.IpAddress() }, Resources.PayloadAssemblerTimeout).ConfigureAwait(false); + content.Metadata!.PayloadId = payloadId.ToString(); _uploadQueue.Queue(content.Metadata); - await _payloadAssembler.Queue(correlationId, content.Metadata, Resources.PayloadAssemblerTimeout).ConfigureAwait(false); _logger.QueuedStowInstance(); content.StatusCode = StatusCodes.Status201Created; @@ -97,7 +100,7 @@ public async Task StoreAsync(HttpRequest request, string correl private IFHirRequestReader GetRequestReader(MediaTypeHeaderValue mediaTypeHeaderValue) { - Guard.Against.Null(mediaTypeHeaderValue); + Guard.Against.Null(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue)); var scope = _serviceScopeFactory.CreateScope(); if (mediaTypeHeaderValue.MediaType.Equals(ContentTypes.ApplicationFhirJson, StringComparison.OrdinalIgnoreCase)) @@ -115,4 +118,4 @@ private IFHirRequestReader GetRequestReader(MediaTypeHeaderValue mediaTypeHeader throw new UnsupportedContentTypeException($"Media type of '{mediaTypeHeaderValue.MediaType}' is not supported."); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Fhir/FhirStoreException.cs b/src/InformaticsGateway/Services/Fhir/FhirStoreException.cs old mode 100644 new mode 100755 index 31d90de27..cde0030a3 --- a/src/InformaticsGateway/Services/Fhir/FhirStoreException.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirStoreException.cs @@ -15,20 +15,18 @@ */ using System; -using System.Runtime.Serialization; namespace Monai.Deploy.InformaticsGateway.Services.Fhir { - [Serializable] public class FhirStoreException : Exception { - public OperationOutcome OperationOutcome { get; } + public OperationOutcome OperationOutcome { get; } = new(); public FhirStoreException(string correlationId, string message, IssueType issueType) : this(correlationId, message, issueType, null) { } - public FhirStoreException(string correlationId, string message, IssueType issueType, Exception innerException) : base(message, innerException) + public FhirStoreException(string correlationId, string message, IssueType issueType, Exception? innerException) : base(message, innerException) { OperationOutcome = new OperationOutcome() { @@ -47,9 +45,5 @@ public FhirStoreException(string correlationId, string message, IssueType issueT Text = message }); } - - protected FhirStoreException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } diff --git a/src/InformaticsGateway/Services/Fhir/FhirStoreResult.cs b/src/InformaticsGateway/Services/Fhir/FhirStoreResult.cs old mode 100644 new mode 100755 index 7139d3b32..6755b849b --- a/src/InformaticsGateway/Services/Fhir/FhirStoreResult.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirStoreResult.cs @@ -28,21 +28,21 @@ internal class FhirStoreResult /// /// User posted data. /// - public string RawData { get; internal set; } + public string RawData { get; internal set; } = string.Empty; /// /// /// - public FhirFileStorageMetadata Metadata { get; internal set; } + public FhirFileStorageMetadata? Metadata { get; internal set; } /// /// ResourceType found in the original FHIR resource. /// - public string InternalResourceType { get; internal set; } + public string InternalResourceType { get; internal set; } = string.Empty; /// /// ResourceType specified by the user in the POST URI. /// - public string ResourceType { get; internal set; } + public string ResourceType { get; internal set; } = string.Empty; } } diff --git a/src/InformaticsGateway/Services/Fhir/FhirXmlReader.cs b/src/InformaticsGateway/Services/Fhir/FhirXmlReader.cs index 123e24e20..385a843ba 100644 --- a/src/InformaticsGateway/Services/Fhir/FhirXmlReader.cs +++ b/src/InformaticsGateway/Services/Fhir/FhirXmlReader.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.Fhir { @@ -48,11 +49,11 @@ public FhirXmlReader(ILogger logger, IOptions GetContentAsync(HttpRequest request, string correlationId, string resourceType, MediaTypeHeaderValue mediaTypeHeaderValue, CancellationToken cancellationToken) { - Guard.Against.Null(request); - Guard.Against.NullOrWhiteSpace(correlationId); + Guard.Against.Null(request, nameof(request)); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); Guard.Against.NullOrInvalidInput(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue), (value) => { - return value.MediaType.Value.Equals(ContentTypes.ApplicationFhirXml, StringComparison.OrdinalIgnoreCase); + return value.MediaType.Value!.Equals(ContentTypes.ApplicationFhirXml, StringComparison.OrdinalIgnoreCase); }); _logger.ParsingFhirXml(); @@ -67,7 +68,7 @@ public async Task GetContentAsync(HttpRequest request, string c xmlDocument.LoadXml(result.RawData); var rootNode = xmlDocument.DocumentElement; - if (!rootNode.NamespaceURI.Equals(Resources.FhirXmlNamespace, StringComparison.OrdinalIgnoreCase)) + if (!rootNode!.NamespaceURI.Equals(Resources.FhirXmlNamespace, StringComparison.OrdinalIgnoreCase)) { throw new FhirStoreException(correlationId, $"XML content is not a valid FHIR resource.", IssueType.Invalid); } @@ -86,7 +87,7 @@ public async Task GetContentAsync(HttpRequest request, string c result.RawData = sb.ToString(); result.InternalResourceType = rootNode.Name; - var fileMetadata = new FhirFileStorageMetadata(correlationId, result.InternalResourceType, resourceId, Api.Rest.FhirStorageFormat.Xml); + var fileMetadata = new FhirFileStorageMetadata(correlationId, result.InternalResourceType, resourceId, Api.Rest.FhirStorageFormat.Xml, DataService.FHIR, request.HttpContext.Connection.RemoteIpAddress!.ToString()); await fileMetadata.SetDataStream(result.RawData, _options.Value.Storage.TemporaryDataStorage, _fileSystem, _options.Value.Storage.LocalTemporaryStoragePath); result.Metadata = fileMetadata; @@ -95,9 +96,9 @@ public async Task GetContentAsync(HttpRequest request, string c private static string SetIdIfMIssing(string correlationId, XmlNamespaceManager xmlNamespaceManager, XmlElement rootNode) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.Null(xmlNamespaceManager); - Guard.Against.Null(rootNode); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.Null(xmlNamespaceManager, nameof(xmlNamespaceManager)); + Guard.Against.Null(rootNode, nameof(rootNode)); var idNode = rootNode.SelectSingleNode($"{Resources.XmlNamespacePrefix}:{Resources.PropertyId}", xmlNamespaceManager); if (idNode is null) @@ -106,14 +107,14 @@ private static string SetIdIfMIssing(string correlationId, XmlNamespaceManager x rootNode.PrependChild(idNode); } - if (string.IsNullOrWhiteSpace(idNode.Attributes[Resources.AttributeValue]?.Value)) + if (string.IsNullOrWhiteSpace(idNode.Attributes![Resources.AttributeValue]?.Value)) { - var value = idNode.OwnerDocument.CreateAttribute(Resources.AttributeValue); + var value = idNode.OwnerDocument!.CreateAttribute(Resources.AttributeValue); value.Value = correlationId; idNode.Attributes.Append(value); } - return idNode.Attributes[Resources.AttributeValue].Value; + return idNode.Attributes[Resources.AttributeValue]!.Value; } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Fhir/OperationOutcome.cs b/src/InformaticsGateway/Services/Fhir/OperationOutcome.cs old mode 100644 new mode 100755 index 43c503e2c..00888e364 --- a/src/InformaticsGateway/Services/Fhir/OperationOutcome.cs +++ b/src/InformaticsGateway/Services/Fhir/OperationOutcome.cs @@ -26,16 +26,16 @@ namespace Monai.Deploy.InformaticsGateway.Services.Fhir public class OperationOutcome { [DataMember] - public string ResourceType { get; set; } + public string ResourceType { get; set; } = string.Empty; [DataMember] - public string Id { get; set; } + public string Id { get; set; } = string.Empty; [XmlIgnore, JsonPropertyName("issue")] public IList Issues { get; set; } [DataMember, JsonIgnore] - public Issue Issue + public Issue? Issue { get { @@ -44,7 +44,10 @@ public Issue Issue set { Issues.Clear(); - Issues.Add(value); + if (value is not null) + { + Issues.Add(value); + } } } @@ -76,7 +79,7 @@ public Issue() public class IssueDetails { [DataMember] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; } [DataContract(Namespace = Resources.FhirXmlNamespace)] diff --git a/src/InformaticsGateway/Services/HealthLevel7/Hl7SendException.cs b/src/InformaticsGateway/Services/HealthLevel7/Hl7SendException.cs new file mode 100755 index 000000000..ec5518c9e --- /dev/null +++ b/src/InformaticsGateway/Services/HealthLevel7/Hl7SendException.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +{ + internal class Hl7SendException : Exception + { + public Hl7SendException() + { + } + + public Hl7SendException(string message) : base(message) + { + } + + public Hl7SendException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs b/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs old mode 100644 new mode 100755 index e2d145b6e..3640227a2 --- a/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/IMllpClientFactory.cs @@ -18,7 +18,7 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { internal interface IMllpClientFactory { @@ -27,6 +27,7 @@ internal interface IMllpClientFactory internal class MllpClientFactory : IMllpClientFactory { - public IMllpClient CreateClient(ITcpClientAdapter client, Hl7Configuration configurations, ILogger logger) => new MllpClient(client, configurations, logger); + public IMllpClient CreateClient(ITcpClientAdapter client, Hl7Configuration configurations, ILogger logger) + => new MllpClient(client, configurations, logger); } } diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs old mode 100644 new mode 100755 index 4160b8ba9..c7e0ffc0b --- a/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpClient.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,20 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Ardalis.GuardClauses; using HL7.Dotnetcore; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; -namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 +namespace Monai.Deploy.InformaticsGateway.Api.Mllp { - internal sealed class MllpClient : IDisposable, IMllpClient + internal sealed class MllpClient : IMllpClient { private readonly ITcpClientAdapter _client; private readonly Hl7Configuration _configurations; @@ -42,6 +41,11 @@ internal sealed class MllpClient : IDisposable, IMllpClient public Guid ClientId { get; } + public string ClientIp + { + get { return _client.RemoteEndPoint.ToString() ?? string.Empty; } + } + public MllpClient(ITcpClientAdapter client, Hl7Configuration configurations, ILogger logger) { _client = client ?? throw new ArgumentNullException(nameof(client)); @@ -49,10 +53,10 @@ public MllpClient(ITcpClientAdapter client, Hl7Configuration configurations, ILo _logger = logger ?? throw new ArgumentNullException(nameof(logger)); ClientId = Guid.NewGuid(); - _exceptions = new List(); - _messages = new List(); + _exceptions = []; + _messages = []; - _loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Endpoint", _client.RemoteEndPoint }, { "CorrelationId", ClientId } }); + _loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Endpoint", _client.RemoteEndPoint }, { "CorrelationId", ClientId } })!; } public async Task Start(Func onDisconnect, CancellationToken cancellationToken) @@ -66,13 +70,13 @@ public async Task Start(Func onDisconnect, if (onDisconnect is not null) { - await onDisconnect(this, new MllpClientResult(_messages, _exceptions.Count > 0 ? new AggregateException(_exceptions) : null)); + await onDisconnect(this, new MllpClientResult(_messages, _exceptions.Count > 0 ? new AggregateException(_exceptions) : null)).ConfigureAwait(false); } } private async Task> ReceiveData(INetworkStream clientStream, CancellationToken cancellationToken) { - Guard.Against.Null(clientStream); + Guard.Against.Null(clientStream, nameof(clientStream)); var data = string.Empty; var messages = new List(); @@ -138,13 +142,14 @@ private async Task> ReceiveData(INetworkStream clientStream, Canc } } while (true); } + linkedCancellationTokenSource.Dispose(); return messages; } private async Task SendAcknowledgment(INetworkStream clientStream, Message message, CancellationToken cancellationToken) { - Guard.Against.Null(clientStream); - Guard.Against.Null(message); + Guard.Against.Null(clientStream, nameof(clientStream)); + Guard.Against.Null(message, nameof(message)); if (!_configurations.SendAcknowledgment) { @@ -153,13 +158,31 @@ private async Task SendAcknowledgment(INetworkStream clientStream, Message messa if (ShouldSendAcknowledgment(message)) { - var ackMessage = message.GetACK(); + Message ackMessage = message; + try + { + ackMessage = message.GetACK(false); + } + catch (Exception ex) + { + _logger.ErrorGeneratingHl7Acknowledgment(ex, message.HL7Message); + _exceptions.Add(ex); + return; + } + + if (ackMessage is null) + { + var ex = new Exception("Error generating HL7 acknowledgment."); + _logger.ErrorGeneratingHl7Acknowledgment(ex, message.HL7Message); + _exceptions.Add(ex); + return; + } var ackData = new ReadOnlyMemory(ackMessage.GetMLLP()); try { await clientStream.WriteAsync(ackData, cancellationToken).ConfigureAwait(false); await clientStream.FlushAsync(cancellationToken).ConfigureAwait(false); - _logger.AcknowledgmentSent(ackData.Length); + _logger.AcknowledgmentSent(ackMessage.HL7Message, ackData.Length); } catch (Exception ex) { @@ -171,11 +194,11 @@ private async Task SendAcknowledgment(INetworkStream clientStream, Message messa private bool ShouldSendAcknowledgment(Message message) { - Guard.Against.Null(message); + Guard.Against.Null(message, nameof(message)); try { var value = message.DefaultSegment(Resources.MessageHeaderSegment).Fields(Resources.AcceptAcknowledgementType); - if (value is null) + if (value is null || string.IsNullOrWhiteSpace(value.Value)) { return true; } @@ -185,8 +208,8 @@ private bool ShouldSendAcknowledgment(Message message) return value.Value switch { Resources.AcknowledgmentTypeNever => false, - Resources.AcknowledgmentTypeError => _exceptions.Any(), - Resources.AcknowledgmentTypeSuccessful => !_exceptions.Any(), + Resources.AcknowledgmentTypeError => _exceptions.Count is not 0, + Resources.AcknowledgmentTypeSuccessful => _exceptions.Count is 0, _ => true, }; } @@ -205,15 +228,15 @@ private bool CreateMessage(int startIndex, int endIndex, ref string data, out Me try { var text = data.Substring(messageStartIndex, endIndex - messageStartIndex); - _logger.Hl7GenerateMessage(text.Length); + _logger.Hl7GenerateMessage(text.Length, text); message = new Message(text); - message.ParseMessage(); + message.ParseMessage(false); data = data.Length > endIndex ? data.Substring(messageEndIndex) : string.Empty; return true; } catch (Exception ex) { - message = null; + message = new(); _logger.ErrorParsingHl7Message(ex); _exceptions.Add(ex); return false; @@ -242,4 +265,4 @@ public void Dispose() GC.SuppressFinalize(this); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs new file mode 100755 index 000000000..184c4f2b8 --- /dev/null +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpExtract.cs @@ -0,0 +1,171 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using FellowOakDicom; +using HL7.Dotnetcore; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Api.Mllp +{ + public sealed class MllpExtract : IMllpExtract + { + private readonly ILogger _logger; + private readonly IHl7ApplicationConfigRepository _hl7ApplicationConfigRepository; + private readonly IExternalAppDetailsRepository _externalAppDetailsRepository; + + public MllpExtract(IHl7ApplicationConfigRepository hl7ApplicationConfigRepository, IExternalAppDetailsRepository externalAppDetailsRepository, ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _hl7ApplicationConfigRepository = hl7ApplicationConfigRepository ?? throw new ArgumentNullException(nameof(hl7ApplicationConfigRepository)); + _externalAppDetailsRepository = externalAppDetailsRepository ?? throw new ArgumentNullException(nameof(externalAppDetailsRepository)); + } + + + public async Task ExtractInfo(Hl7FileStorageMetadata meta, Message message, Hl7ApplicationConfigEntity configItem) + { + try + { + // extract data for the given fields + // Use Id to get record from Db + var details = await GetExtAppDetails(configItem, message).ConfigureAwait(false); + + if (details is null) + { + _logger.Hl7ExtAppDetailsNotFound(); + return message; + } + + // fill in meta data with workflowInstance and Task ID + // repopulate message with data from record + + meta.WorkflowInstanceId = details.WorkflowInstanceId; + meta.TaskId = details.ExportTaskID; + meta.ChangeCorrelationId(_logger, details.CorrelationId); + + if (string.IsNullOrEmpty(details.DestinationFolder) is false) + { + meta.File.DestinationFolderOverride = true; + meta.File.UploadPath = $"{details.DestinationFolder}/{meta.DataTypeDirectoryName}/{meta.Id}{Hl7FileStorageMetadata.FileExtension}"; + } + message = RepopulateMessage(configItem, details, message); + } + catch (Exception ex) + { + _logger.Hl7ExceptionThrow(ex); + } + return message; + } + + public async Task GetConfigItem(Message message) + { + // load the config + var config = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); + if (config == null) + { + _logger.Hl7NoConfig(); + return null; + } + _logger.Hl7ConfigLoaded($"Config: {JsonSerializer.Serialize(config)}"); + // get config for vendorId + var configItem = GetConfig(config, message); + if (configItem == null) + { + _logger.Hl7NoMatchingConfig(message.HL7Message); + return null; + } + return configItem; + } + + private async Task GetExtAppDetails(Hl7ApplicationConfigEntity hl7ApplicationConfigEntity, Message message) + { + var tagId = message.GetValue(hl7ApplicationConfigEntity.DataLink.Key); + var type = hl7ApplicationConfigEntity.DataLink.Value; + switch (type) + { + case DataLinkType.PatientId: + return await _externalAppDetailsRepository.GetByPatientIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); + case DataLinkType.StudyInstanceUid: + return await _externalAppDetailsRepository.GetByStudyIdOutboundAsync(tagId, new CancellationToken()).ConfigureAwait(false); + default: + break; + } + + throw new Exception($"Invalid DataLinkType: {type}"); + } + + internal Hl7ApplicationConfigEntity? GetConfig(List config, Message message) + { + foreach (var item in config) + { + var sendingId = message.GetValue(item.SendingId.Key); + if (item.SendingId.Value == sendingId) + { + _logger.Hl7FoundMatchingConfig(sendingId, JsonSerializer.Serialize(item)); + return item; + } + else + { + _logger.Hl7NotMatchingConfig(sendingId, item.SendingId.Value); + } + } + return null; + } + + private Message RepopulateMessage(Hl7ApplicationConfigEntity config, ExternalAppDetails details, Message message) + { + foreach (var item in config.DataMapping) + { + var tag = DicomTag.Parse(item.Value); + // these are the only two fields we have at the point + if (tag == DicomTag.PatientID) + { + var oldvalue = message.GetValue(item.Key); + message.SetValue(item.Key, details.PatientId); + _logger.ChangingHl7Values(item.Key, oldvalue, details.PatientId); + if (message.HL7Message.Contains(oldvalue)) + { + var newMess = message.HL7Message.Replace(oldvalue, details.PatientId); + message = new Message(newMess); + message.ParseMessage(true); + } + } + else if (tag == DicomTag.StudyInstanceUID) + { + var oldvalue = message.GetValue(item.Key); + message.SetValue(item.Key, details.StudyInstanceUid); + _logger.ChangingHl7Values(item.Key, oldvalue, details.StudyInstanceUid); + if (message.HL7Message.Contains(oldvalue)) + { + var newMess = message.HL7Message.Replace(oldvalue, details.StudyInstanceUid); + message = new Message(newMess); + message.ParseMessage(true); + } + } + } + return message; + } + } +} diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs old mode 100644 new mode 100755 index c2be31a00..0ec95941c --- a/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpService.cs @@ -1,5 +1,5 @@ -/* - * Copyright 2022 MONAI Consortium +/* + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,212 +15,111 @@ */ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO.Abstractions; +using System.Net; +using System.Net.Sockets; +using System.Text; using System.Threading; using System.Threading.Tasks; -using Ardalis.GuardClauses; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using HL7.Dotnetcore; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api.Rest; -using Monai.Deploy.InformaticsGateway.Api.Storage; -using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.InformaticsGateway.Services.Common; -using Monai.Deploy.InformaticsGateway.Services.Connectors; -using Monai.Deploy.InformaticsGateway.Services.Storage; namespace Monai.Deploy.InformaticsGateway.Services.HealthLevel7 { - internal sealed class MllpService : IHostedService, IDisposable, IMonaiService + internal class MllpService : IMllpService { - private const int SOCKET_OPERATION_CANCELLED = 125; - private bool _disposedValue; - private readonly ITcpListener _tcpListener; - private readonly IMllpClientFactory _mllpClientFactory; - private readonly IObjectUploadQueue _uploadQueue; - private readonly IPayloadAssembler _payloadAssembler; - private readonly IFileSystem _fileSystem; - private readonly IServiceScope _serviceScope; - private readonly ILoggerFactory _logginFactory; + private readonly ILogger _logger; - private readonly IOptions _configuration; - private readonly IStorageInfoProvider _storageInfoProvider; - private readonly ConcurrentDictionary _activeTasks; + private readonly InformaticsGatewayConfiguration _configuration; - public int ActiveConnections + public MllpService(ILogger logger, IOptions configuration) { - get - { - return _activeTasks.Count; - } + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _configuration = configuration?.Value ?? throw new ArgumentNullException(nameof(configuration)); ; } - public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; - - public string ServiceName => "HL7 Service"; - - public MllpService(IServiceScopeFactory serviceScopeFactory, - IOptions configuration) + public async Task SendMllp(IPAddress address, int port, string hl7Message, CancellationToken cancellationToken) { - if (serviceScopeFactory is null) + try { - throw new ArgumentNullException(nameof(serviceScopeFactory)); + var body = $"{Resources.AsciiVT}{hl7Message}{Resources.AsciiFS}{Resources.AcsiiCR}"; + var sendMessageByteBuffer = Encoding.UTF8.GetBytes(body); + await WriteMessage(sendMessageByteBuffer, address, port).ConfigureAwait(false); + } + catch (ArgumentOutOfRangeException) + { + _logger.Hl7AckMissingStartOrEndCharacters(); + throw new Hl7SendException("ACK missing start or end characters"); + } + catch (Exception ex) + { + _logger.Hl7SendException(ex); + throw new Hl7SendException($"Send exception: {ex.Message}"); } - - _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - - _serviceScope = serviceScopeFactory.CreateScope(); - _logginFactory = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ILoggerFactory)); - _logger = _logginFactory.CreateLogger(); - var tcpListenerFactory = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ITcpListenerFactory)); - _tcpListener = tcpListenerFactory.CreateTcpListener(System.Net.IPAddress.Any, _configuration.Value.Hl7.Port); - _mllpClientFactory = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IMllpClientFactory)); - _uploadQueue = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IObjectUploadQueue)); - _payloadAssembler = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadAssembler)); - _fileSystem = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IFileSystem)); - _storageInfoProvider = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageInfoProvider)); - _activeTasks = new ConcurrentDictionary(); } - public Task StartAsync(CancellationToken cancellationToken) + private async Task WriteMessage(byte[] sendMessageByteBuffer, IPAddress address, int port) { - var task = Task.Run(async () => - { - _tcpListener.Start(); - await BackgroundProcessing(cancellationToken).ConfigureAwait(true); - }, CancellationToken.None); - Status = ServiceStatus.Running; - _logger.ServiceRunning(ServiceName); - _logger.Hl7ListeningOnPort(_configuration.Value.Hl7.Port); + using var tcpClient = new TcpClient(); - if (task.IsCompleted) - return task; - return Task.CompletedTask; - } + tcpClient.Connect(address, port); - public Task StopAsync(CancellationToken cancellationToken) - { - _logger.ServiceStopping(ServiceName); - _tcpListener.Stop(); - Status = ServiceStatus.Stopped; - return Task.CompletedTask; - } + var networkStream = new NetworkStream(tcpClient.Client); - private async Task BackgroundProcessing(CancellationToken cancellationToken) - { - while (!cancellationToken.IsCancellationRequested) + if (networkStream.CanWrite) { - IMllpClient mllpClient = null; - try - { - WaitUntilAvailable(_configuration.Value.Hl7.MaximumNumberOfConnections); - var client = await _tcpListener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(false); - _logger.ClientConnected(); - - if (!_storageInfoProvider.HasSpaceAvailableToStore) - { - _logger.Hl7DisconnectedDueToLowStorageSpace(_storageInfoProvider.AvailableFreeSpace); - client.Close(); - await Task.Delay(5000, cancellationToken).ConfigureAwait(false); - continue; - } - - mllpClient = _mllpClientFactory.CreateClient(client, _configuration.Value.Hl7, _logginFactory.CreateLogger()); - _ = mllpClient.Start(OnDisconnect, cancellationToken); - _activeTasks.TryAdd(mllpClient.ClientId, mllpClient); - } - catch (System.Net.Sockets.SocketException ex) - { - _logger.Hl7SocketException(ex.Message); - - if (mllpClient is not null) - { - mllpClient.Dispose(); - _activeTasks.Remove(mllpClient.ClientId, out _); - } - - if (ex.ErrorCode == SOCKET_OPERATION_CANCELLED) - { - break; - } - } - catch (Exception ex) - { - _logger.ServiceInvalidOrCancelled(ServiceName, ex); - } + networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length); + networkStream.Flush(); } - Status = ServiceStatus.Cancelled; - _logger.ServiceCancelled(ServiceName); + else + { + _logger.Hl7ClientStreamNotWritable(); + throw new Hl7SendException("Client stream not writable"); + } + + _logger.Hl7MessageSent(Encoding.UTF8.GetString(sendMessageByteBuffer)); + + await EnsureAck(networkStream).ConfigureAwait(false); } - private async Task OnDisconnect(IMllpClient client, MllpClientResult result) + private async Task EnsureAck(NetworkStream networkStream) { - Guard.Against.Null(client); - Guard.Against.Null(result); + using var s_cts = new CancellationTokenSource(); + s_cts.CancelAfter(_configuration.Hl7.ClientTimeoutMilliseconds); + var buffer = new byte[2048]; + // get the SentHl7Message + networkStream.ReadTimeout = 5000; + networkStream.WriteTimeout = 5000; - try - { - foreach (var message in result.Messages) - { - var hl7Fileetadata = new Hl7FileStorageMetadata(client.ClientId.ToString()); - await hl7Fileetadata.SetDataStream(message.HL7Message, _configuration.Value.Storage.TemporaryDataStorage, _fileSystem, _configuration.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); - _uploadQueue.Queue(hl7Fileetadata); - await _payloadAssembler.Queue(client.ClientId.ToString(), hl7Fileetadata).ConfigureAwait(false); - } - } - catch (Exception ex) - { - _logger.ErrorHandlingHl7Results(ex); - } - finally + // wait for responce + while (!s_cts.IsCancellationRequested && networkStream.DataAvailable == false) { - _activeTasks.Remove(client.ClientId, out _); - _logger.Hl7ClientRemoved(client.ClientId); - client.Dispose(); + await Task.Delay(20).ConfigureAwait(false); } - } - private void WaitUntilAvailable(int maximumNumberOfConnections) - { - var count = 0; - while (ActiveConnections >= maximumNumberOfConnections) + var bytesRead = await networkStream.ReadAsync(buffer).ConfigureAwait(false); + + if (bytesRead == 0 || s_cts.IsCancellationRequested) { - if (++count % 25 == 1) - { - _logger.MaxedOutHl7Connections(maximumNumberOfConnections); - } - Thread.Sleep(100); + throw new Hl7SendException("ACK message contains no ACK!"); } - } - private void Dispose(bool disposing) - { - if (!_disposedValue) + var _rawHl7Messages = MessageHelper.ExtractMessages(Encoding.UTF8.GetString(buffer)); + foreach (var message in _rawHl7Messages) { - if (disposing) + var hl7Message = new Message(message); + hl7Message.ParseMessage(false); + if (hl7Message.MessageStructure == "ACK") { - foreach (var client in _activeTasks.Values) - { - client.Dispose(); - } + return; } - - _disposedValue = true; } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); + throw new Hl7SendException("ACK message contains no ACK!"); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/HealthLevel7/MllpServiceHost.cs b/src/InformaticsGateway/Services/HealthLevel7/MllpServiceHost.cs new file mode 100755 index 000000000..4b90666c5 --- /dev/null +++ b/src/InformaticsGateway/Services/HealthLevel7/MllpServiceHost.cs @@ -0,0 +1,269 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Services.Connectors; +using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Events; + +namespace Monai.Deploy.InformaticsGateway.Api.Mllp +{ + internal sealed class MllpServiceHost : IHostedService, IDisposable, IMonaiService + { + private const int SOCKET_OPERATION_CANCELLED = 125; + private bool _disposedValue; + private readonly ITcpListener _tcpListener; + private readonly IMllpClientFactory _mllpClientFactory; + private readonly IObjectUploadQueue _uploadQueue; + private readonly IPayloadAssembler _payloadAssembler; + private readonly IFileSystem _fileSystem; + private readonly ILoggerFactory _logginFactory; + private readonly ILogger _logger; + private readonly IOptions _configuration; + private readonly IStorageInfoProvider _storageInfoProvider; + private readonly ConcurrentDictionary _activeTasks; + private readonly IMllpExtract _mIIpExtract; + private readonly IInputHL7DataPlugInEngine _inputHL7DataPlugInEngine; + private readonly IHl7ApplicationConfigRepository _hl7ApplicationConfigRepository; + private DateTime _lastConfigRead = new(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public int ActiveConnections + { + get + { + return _activeTasks.Count; + } + } + + public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; + + public string ServiceName => "HL7 Service"; + + public MllpServiceHost(IServiceScopeFactory serviceScopeFactory, IOptions configuration) + { + ArgumentNullException.ThrowIfNull(serviceScopeFactory, nameof(serviceScopeFactory)); + + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + + var serviceScope = serviceScopeFactory.CreateScope(); + _logginFactory = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ILoggerFactory)); + _logger = _logginFactory.CreateLogger(); + var tcpListenerFactory = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ITcpListenerFactory)); + _tcpListener = tcpListenerFactory.CreateTcpListener(System.Net.IPAddress.Any, _configuration.Value.Hl7.Port); + _mllpClientFactory = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IMllpClientFactory)); + _uploadQueue = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IObjectUploadQueue)); + _payloadAssembler = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadAssembler)); + _fileSystem = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IFileSystem)); + _storageInfoProvider = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageInfoProvider)); + _mIIpExtract = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IMllpExtract)); + _activeTasks = new ConcurrentDictionary(); + _inputHL7DataPlugInEngine = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IInputHL7DataPlugInEngine)); + _hl7ApplicationConfigRepository = serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IHl7ApplicationConfigRepository)); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + var task = Task.Run(async () => + { + _tcpListener.Start(); + await BackgroundProcessing(cancellationToken).ConfigureAwait(true); + }, CancellationToken.None); + + Status = ServiceStatus.Running; + _logger.ServiceRunning(ServiceName); + _logger.Hl7ListeningOnPort(_configuration.Value.Hl7.Port); + + if (task.IsCompleted) + return task; + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.ServiceStopping(ServiceName); + _tcpListener.Stop(); + Status = ServiceStatus.Stopped; + return Task.CompletedTask; + } + + private async Task BackgroundProcessing(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + { + IMllpClient? mllpClient = null; + try + { + WaitUntilAvailable(_configuration.Value.Hl7.MaximumNumberOfConnections); + var client = await _tcpListener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(false); + _logger.ClientConnected(); + + if (!_storageInfoProvider.HasSpaceAvailableToStore) + { + _logger.Hl7DisconnectedDueToLowStorageSpace(_storageInfoProvider.AvailableFreeSpace); + client.Close(); + await Task.Delay(5000, cancellationToken).ConfigureAwait(false); + continue; + } + + mllpClient = _mllpClientFactory.CreateClient(client, _configuration.Value.Hl7, _logginFactory.CreateLogger()); + _ = mllpClient.Start(OnDisconnect, cancellationToken); + _activeTasks.TryAdd(mllpClient.ClientId, mllpClient); + } + catch (System.Net.Sockets.SocketException ex) + { + _logger.Hl7SocketException(ex.Message); + + if (mllpClient is not null) + { + mllpClient.Dispose(); + _activeTasks.Remove(mllpClient.ClientId, out _); + } + + if (ex.ErrorCode == SOCKET_OPERATION_CANCELLED) + { + break; + } + } + catch (Exception ex) + { + _logger.ServiceInvalidOrCancelled(ServiceName, ex); + } + } + Status = ServiceStatus.Cancelled; + _logger.ServiceCancelled(ServiceName); + } + + private async Task OnDisconnect(IMllpClient client, MllpClientResult result) + { + Guard.Against.Null(client, nameof(client)); + Guard.Against.Null(result, nameof(result)); + + await ConfigurePlugInEngine().ConfigureAwait(false); + + try + { + foreach (var message in result.Messages) + { + var newMessage = message; + var hl7Filemetadata = new Hl7FileStorageMetadata(client.ClientId.ToString(), DataService.HL7, client.ClientIp); + var configItem = await _mIIpExtract.GetConfigItem(message).ConfigureAwait(false); + if (configItem is not null) + { + await _inputHL7DataPlugInEngine.ExecutePlugInsAsync(message, hl7Filemetadata, configItem).ConfigureAwait(false); + newMessage = await _mIIpExtract.ExtractInfo(hl7Filemetadata, message, configItem).ConfigureAwait(false); + + _logger.HL7MessageAfterPluginProcessing(newMessage.HL7Message, hl7Filemetadata.CorrelationId); + } + _logger.Hl7MessageReceieved(newMessage.HL7Message); + await hl7Filemetadata.SetDataStream(newMessage.HL7Message, _configuration.Value.Storage.TemporaryDataStorage, _fileSystem, _configuration.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); + var payloadId = await _payloadAssembler.Queue(client.ClientId.ToString(), hl7Filemetadata, new DataOrigin { DataService = DataService.HL7, Source = client.ClientIp, Destination = FileStorageMetadata.IpAddress() }).ConfigureAwait(false); + hl7Filemetadata.PayloadId ??= payloadId.ToString(); + _uploadQueue.Queue(hl7Filemetadata); + } + } + catch (Exception ex) + { + _logger.ErrorHandlingHl7Results(ex); + } + finally + { + _activeTasks.Remove(client.ClientId, out _); + _logger.Hl7ClientRemoved(client.ClientId); + client.Dispose(); + } + } + + private async Task ConfigurePlugInEngine() + { + var configs = await _hl7ApplicationConfigRepository.GetAllAsync().ConfigureAwait(false); + if (configs is not null && configs.Count is not 0 && configs.Max(c => c.LastModified) > _lastConfigRead) + { + var pluginAssemblies = new List(); + foreach (var config in configs.Where(p => p.PlugInAssemblies?.Count > 0)) + { + try + { + pluginAssemblies.AddRange(config.PlugInAssemblies.Where(p => string.IsNullOrWhiteSpace(p) is false && pluginAssemblies.Contains(p) is false)); + } + catch (Exception ex) + { + _logger.HL7PluginLoadingExceptions(ex); + } + } + if (pluginAssemblies.Count is not 0) + { + _inputHL7DataPlugInEngine.Configure(pluginAssemblies); + } + } + _lastConfigRead = DateTime.UtcNow; + } + + private void WaitUntilAvailable(int maximumNumberOfConnections) + { + var count = 0; + while (ActiveConnections >= maximumNumberOfConnections) + { + if (++count % 25 == 1) + { + _logger.MaxedOutHl7Connections(maximumNumberOfConnections); + } + Thread.Sleep(100); + } + } + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + foreach (var client in _activeTasks.Values) + { + client.Dispose(); + } + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/InformaticsGateway/Services/HealthLevel7/Resources.cs b/src/InformaticsGateway/Services/HealthLevel7/Resources.cs old mode 100644 new mode 100755 index 62b0ed23a..f3b30bf3c --- a/src/InformaticsGateway/Services/HealthLevel7/Resources.cs +++ b/src/InformaticsGateway/Services/HealthLevel7/Resources.cs @@ -22,6 +22,7 @@ internal static class Resources public const char AsciiVT = (char)0x0B; public const char AsciiFS = (char)0x1C; + public const char AcsiiCR = (char)13; public const string AcknowledgmentTypeNever = "NE"; public const string AcknowledgmentTypeError = "ER"; diff --git a/src/InformaticsGateway/Services/Http/ApiControllerBase.cs b/src/InformaticsGateway/Services/Http/ApiControllerBase.cs new file mode 100644 index 000000000..6ecd5050f --- /dev/null +++ b/src/InformaticsGateway/Services/Http/ApiControllerBase.cs @@ -0,0 +1,51 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Net; +using Microsoft.AspNetCore.Mvc; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + /// + /// Base Api Controller. + /// + [ApiController] + public class ApiControllerBase : ControllerBase + { + /// + /// Initializes a new instance of the class. + /// + public ApiControllerBase() + { + } + + /// + /// Gets internal Server Error 500. + /// + public static int InternalServerError => (int)HttpStatusCode.InternalServerError; + + /// + /// Gets bad Request 400. + /// + public static new int BadRequest => (int)HttpStatusCode.BadRequest; + + /// + /// Gets notFound 404. + /// + public static new int NotFound => (int)HttpStatusCode.NotFound; + } +} diff --git a/src/InformaticsGateway/Services/Http/DestinationAeTitleController.cs b/src/InformaticsGateway/Services/Http/DestinationAeTitleController.cs old mode 100644 new mode 100755 index 09326de1d..91e46723d --- a/src/InformaticsGateway/Services/Http/DestinationAeTitleController.cs +++ b/src/InformaticsGateway/Services/Http/DestinationAeTitleController.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2020 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,10 +23,13 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Scu; namespace Monai.Deploy.InformaticsGateway.Services.Http @@ -37,16 +40,19 @@ public class DestinationAeTitleController : ControllerBase { private readonly ILogger _logger; private readonly IDestinationApplicationEntityRepository _repository; + private readonly IDataPlugInEngineFactory _outputDataPlugInEngineFactory; private readonly IScuQueue _scuQueue; public DestinationAeTitleController( ILogger logger, IDestinationApplicationEntityRepository repository, - IScuQueue scuQueue) + IScuQueue scuQueue, + IDataPlugInEngineFactory outputDataPlugInEngineFactory) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _repository = repository ?? throw new ArgumentNullException(nameof(repository)); _scuQueue = scuQueue ?? throw new ArgumentNullException(nameof(scuQueue)); + _outputDataPlugInEngineFactory = outputDataPlugInEngineFactory ?? throw new ArgumentNullException(nameof(outputDataPlugInEngineFactory)); } [HttpGet] @@ -108,14 +114,21 @@ public async Task CEcho(string name) return NotFound(); } - var destinationApplicationEntity = await _repository.FindByNameAsync(name, HttpContext.RequestAborted).ConfigureAwait(false); + var destinationApplicationEntity = await _repository.FindByNameAsync(name, HttpContext!.RequestAborted).ConfigureAwait(false); if (destinationApplicationEntity is null) { return NotFound(); } - var request = new ScuWorkRequest(traceId, RequestType.CEcho, destinationApplicationEntity.HostIp, destinationApplicationEntity.Port, destinationApplicationEntity.AeTitle); + var request = new ScuWorkRequest( + traceId, + RequestType.CEcho, + destinationApplicationEntity.HostIp, + destinationApplicationEntity.Port, + destinationApplicationEntity.AeTitle, + HttpContext.RequestAborted + ); var response = await _scuQueue.Queue(request, HttpContext.RequestAborted).ConfigureAwait(false); if (response.Status != ResponseStatus.Success) @@ -152,6 +165,7 @@ public async Task> Create(DestinationApplicationEntity item try { item.SetDefaultValues(); + item.SetAuthor(User, EditMode.Create); await ValidateCreateAsync(item).ConfigureAwait(false); @@ -179,7 +193,7 @@ public async Task> Create(DestinationApplicationEntity item [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> Edit(DestinationApplicationEntity item) + public async Task> Edit(DestinationApplicationEntity? item) { try { @@ -199,6 +213,7 @@ public async Task> Edit(DestinationAp destinationApplicationEntity.AeTitle = item.AeTitle; destinationApplicationEntity.HostIp = item.HostIp; destinationApplicationEntity.Port = item.Port; + destinationApplicationEntity.SetAuthor(User, EditMode.Update); await ValidateUpdateAsync(destinationApplicationEntity).ConfigureAwait(false); @@ -244,6 +259,23 @@ public async Task> Delete(string name } } + [HttpGet("plug-ins")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public ActionResult GetPlugIns() + { + try + { + return Ok(_outputDataPlugInEngineFactory.RegisteredPlugIns()); + } + catch (Exception ex) + { + _logger.ErrorReadingDataInputPlugIns(ex); + return Problem(title: "Error reading data input plug-ins.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + private async Task ValidateCreateAsync(DestinationApplicationEntity item) { if (await _repository.ContainsAsync(p => p.Name.Equals(item.Name), HttpContext.RequestAborted).ConfigureAwait(false)) diff --git a/src/InformaticsGateway/Services/Http/DicomAssociationInfoController.cs b/src/InformaticsGateway/Services/Http/DicomAssociationInfoController.cs new file mode 100755 index 000000000..ad0fb4d37 --- /dev/null +++ b/src/InformaticsGateway/Services/Http/DicomAssociationInfoController.cs @@ -0,0 +1,91 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; +using Monai.Deploy.InformaticsGateway.Services.UriService; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + [Route("dicom-associations")] + public class DicomAssociationInfoController : PagedApiControllerBase + { + private const string Endpoint = "/dicom-associations"; + private readonly ILogger _logger; + private readonly IDicomAssociationInfoRepository _dicomRepo; + private readonly IUriService _uriService; + + public DicomAssociationInfoController(ILogger logger, + IOptions options, + IDicomAssociationInfoRepository dicomRepo, + IUriService uriService) : base(options) + { + _logger = logger; + _dicomRepo = dicomRepo; + _uriService = uriService; + } + + /// + /// Gets a paged response list of all workflows. + /// + /// Filters. + /// paged response of subset of all workflows. + [HttpGet] + [ProducesResponseType(typeof(PagedResponse>), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] + public async Task GetAllAsync([FromQuery] TimeFilter filter) + { + try + { + var route = Request?.Path.Value ?? string.Empty; + var pageSize = filter.PageSize ?? EndpointOptions.Value.DefaultPageSize; + var validFilter = new TimeFilter( + filter.StartTime, + filter.EndTime, + filter.PageNumber ?? 0, + pageSize, + EndpointOptions.Value.MaxPageSize); + + var pagedData = await _dicomRepo.GetAllAsync( + validFilter.GetSkip(), + validFilter.PageSize, + filter.StartTime!.Value, + filter.EndTime!.Value, default).ConfigureAwait(false); + + var dataTotal = await _dicomRepo.CountAsync().ConfigureAwait(false); + var pagedResponse = CreatePagedResponse(pagedData.ToList(), validFilter, dataTotal, _uriService, route); + return Ok(pagedResponse); + } + catch (Exception e) + { + _logger.DicomAssociationsControllerGetError(e); + return Problem($"Unexpected error occurred: {e.Message}", Endpoint, InternalServerError); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Http/DicomWeb/StowController.cs b/src/InformaticsGateway/Services/Http/DicomWeb/StowController.cs index 3e33b21c1..861394879 100644 --- a/src/InformaticsGateway/Services/Http/DicomWeb/StowController.cs +++ b/src/InformaticsGateway/Services/Http/DicomWeb/StowController.cs @@ -16,6 +16,7 @@ using System; using System.Threading.Tasks; +using System.Web; using FellowOakDicom; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -51,7 +52,9 @@ public StowController(IServiceScopeFactory serviceScopeFactory) } [HttpPost("studies")] + [HttpPost("vae/{aet}/studies")] [HttpPost("{workflowName}/studies")] + [HttpPost("vae/{aet}/{workflowName}/studies")] [Consumes(ContentTypes.ApplicationDicom, ContentTypes.MultipartRelated)] [Produces(ContentTypes.ApplicationDicomJson)] [ProducesResponseType(typeof(DicomDataset), StatusCodes.Status200OK)] @@ -62,13 +65,15 @@ public StowController(IServiceScopeFactory serviceScopeFactory) [ProducesResponseType(typeof(string), StatusCodes.Status415UnsupportedMediaType)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status507InsufficientStorage)] - public async Task StoreInstances(string workflowName = "") + public async Task StoreInstances(string? aet = "", string? workflowName = "") { - return await StoreInstances(string.Empty, workflowName).ConfigureAwait(false); + return await StoreInstances(string.Empty, aet, workflowName).ConfigureAwait(false); } [HttpPost("studies/{studyInstanceUId}")] + [HttpPost("vae/{aet}/studies/{studyInstanceUId}")] [HttpPost("{workflowName}/studies/{studyInstanceUid}")] + [HttpPost("vae/{aet}/{workflowName}/studies/{studyInstanceUid}")] [Consumes(ContentTypes.ApplicationDicom, ContentTypes.MultipartRelated)] [Produces(ContentTypes.ApplicationDicomJson)] [ProducesResponseType(typeof(DicomDataset), StatusCodes.Status200OK)] @@ -79,15 +84,15 @@ public async Task StoreInstances(string workflowName = "") [ProducesResponseType(typeof(string), StatusCodes.Status415UnsupportedMediaType)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status507InsufficientStorage)] - public async Task StoreInstancesToStudy(string studyInstanceUid, string workflowName = "") + public async Task StoreInstancesToStudy(string? studyInstanceUid, string? aet = "", string? workflowName = "") { - return await StoreInstances(studyInstanceUid, workflowName).ConfigureAwait(false); + return await StoreInstances(studyInstanceUid, aet, workflowName).ConfigureAwait(false); } - private async Task StoreInstances(string studyInstanceUid, string workflowName) + private async Task StoreInstances(string? studyInstanceUid, string? aet, string? workflowName) { var correlationId = Guid.NewGuid().ToString(); - using var logger = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", correlationId }, { "StudyInstanceUID", studyInstanceUid }, { "Workflow", workflowName } }); + using var logger = _logger.BeginScope(new LoggingDataDictionary { { "CorrelationId", correlationId }, { "StudyInstanceUID", studyInstanceUid ?? string.Empty }, { "Workflow", workflowName ?? string.Empty } }); if (!_storageInfoProvider.HasSpaceAvailableToStore) { @@ -98,20 +103,27 @@ private async Task StoreInstances(string studyInstanceUid, string try { - var result = await _stowService.StoreAsync(Request, studyInstanceUid, workflowName, correlationId, HttpContext.RequestAborted).ConfigureAwait(false); + var result = await _stowService.StoreAsync(Request, studyInstanceUid, aet, workflowName, correlationId, HttpContext.RequestAborted).ConfigureAwait(false); return StatusCode(result.StatusCode, result.Data); } catch (DicomValidationException ex) { - _logger.ErrorDicomWebStowInvalidStudyInstanceUid(studyInstanceUid, ex); + _logger.ErrorDicomWebStowInvalidStudyInstanceUid(studyInstanceUid?.Replace(Environment.NewLine, ""), ex); return StatusCode( StatusCodes.Status400BadRequest, Problem(title: $"Invalid StudyInstanceUID provided '{studyInstanceUid}'.", statusCode: StatusCodes.Status400BadRequest, detail: ex.Message)); } + catch (ApplicationEntityNotFoundException ex) + { + _logger.ErrorDicomWebStowUnknownVirtualApplicationEntity(HttpUtility.HtmlEncode(aet), ex); + return StatusCode( + StatusCodes.Status400BadRequest, + Problem(title: $"Invalid virtual application entity '{aet}'.", statusCode: StatusCodes.Status400BadRequest, detail: ex.Message)); + } catch (Exception ex) { - _logger.ErrorDicomWebStow(studyInstanceUid, workflowName, ex); + _logger.ErrorDicomWebStow(studyInstanceUid?.Replace(Environment.NewLine, ""), workflowName?.Replace(Environment.NewLine, ""), ex); return StatusCode( StatusCodes.Status500InternalServerError, Problem(title: "Error.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message)); diff --git a/src/InformaticsGateway/Services/Http/HL7DestinationController.cs b/src/InformaticsGateway/Services/Http/HL7DestinationController.cs new file mode 100755 index 000000000..06ed7a20a --- /dev/null +++ b/src/InformaticsGateway/Services/Http/HL7DestinationController.cs @@ -0,0 +1,239 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2020 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mime; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + [ApiController] + [Route("config/hl7-destination")] + public class HL7DestinationController : ControllerBase + { + private readonly ILogger _logger; + private readonly IHL7DestinationEntityRepository _repository; + + public HL7DestinationController( + ILogger logger, + IHL7DestinationEntityRepository repository) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + } + + [HttpGet] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task>> Get() + { + try + { + return Ok(await _repository.ToListAsync(HttpContext.RequestAborted).ConfigureAwait(false)); + } + catch (Exception ex) + { + _logger.ErrorQueryingDatabase(ex); + return Problem(title: "Error querying database.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + [HttpGet("{name}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ActionName(nameof(GetAeTitle))] + public async Task> GetAeTitle(string name) + { + try + { + var hl7DestinationEntity = await _repository.FindByNameAsync(name, HttpContext.RequestAborted).ConfigureAwait(false); + + if (hl7DestinationEntity is null) + { + return NotFound(); + } + + return Ok(hl7DestinationEntity); + } + catch (Exception ex) + { + _logger.ErrorListingHL7DestinationEntities(ex); + return Problem(title: "Error querying HL7 destinations.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + [HttpGet("cecho/{name}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status502BadGateway)] + [ActionName(nameof(GetAeTitle))] +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + public async Task CEcho(string name) +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously + { + throw new NotImplementedException(); + } + + [HttpPost] + [Consumes(MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Produces("application/json")] + public async Task> Create(HL7DestinationEntity item) + { + try + { + item.SetDefaultValues(); + item.SetAuthor(User, EditMode.Create); + + await ValidateCreateAsync(item).ConfigureAwait(false); + + await _repository.AddAsync(item, HttpContext.RequestAborted).ConfigureAwait(false); + _logger.HL7DestinationEntityAdded(item.Name, item.HostIp); + return CreatedAtAction(nameof(GetAeTitle), new { name = item.Name }, item); + } + catch (ObjectExistsException ex) + { + return Problem(title: "HL7 destination already exists", statusCode: StatusCodes.Status409Conflict, detail: ex.Message); + } + catch (ConfigurationException ex) + { + return Problem(title: "Validation error", statusCode: StatusCodes.Status400BadRequest, detail: ex.Message); + } + catch (Exception ex) + { + _logger.ErrorAddingHL7DestinationEntity(ex); + return Problem(title: "Error adding new HL7 destination", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + [HttpPut] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Edit(HL7DestinationEntity? item) + { + try + { + if (item is null) + { + return NotFound(); + } + + var hl7DestinationEntity = await _repository.FindByNameAsync(item.Name, HttpContext.RequestAborted).ConfigureAwait(false); + if (hl7DestinationEntity is null) + { + return NotFound(); + } + + item.SetDefaultValues(); + + hl7DestinationEntity.HostIp = item.HostIp; + hl7DestinationEntity.Port = item.Port; + hl7DestinationEntity.SetAuthor(User, EditMode.Update); + + await ValidateUpdateAsync(hl7DestinationEntity).ConfigureAwait(false); + + _ = _repository.UpdateAsync(hl7DestinationEntity, HttpContext.RequestAborted); + _logger.HL7DestinationEntityUpdated(item.Name, item.HostIp, item.Port); + return Ok(hl7DestinationEntity); + } + catch (ConfigurationException ex) + { + return Problem(title: "Validation error.", statusCode: (int)System.Net.HttpStatusCode.BadRequest, detail: ex.Message); + } + catch (Exception ex) + { + _logger.ErrorDeletingHL7DestinationEntity(ex); + return Problem(title: "Error updating HL7 destination.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + [HttpDelete("{name}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Delete(string name) + { + try + { + var hl7DestinationEntity = await _repository.FindByNameAsync(name, HttpContext.RequestAborted).ConfigureAwait(false); + if (hl7DestinationEntity is null) + { + return NotFound(); + } + + await _repository.RemoveAsync(hl7DestinationEntity, HttpContext.RequestAborted).ConfigureAwait(false); + + _logger.HL7DestinationEntityDeleted(new string(name.Take(5).ToArray())); + return Ok(hl7DestinationEntity); + } + catch (Exception ex) + { + _logger.ErrorDeletingHL7DestinationEntity(ex); + return Problem(title: "Error deleting HL7 destination.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + private async Task ValidateCreateAsync(HL7DestinationEntity item) + { + if (await _repository.ContainsAsync(p => p.Name.Equals(item.Name), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A HL7 destination with the same name '{item.Name}' already exists."); + } + if (await _repository.ContainsAsync(p => p.HostIp.Equals(item.HostIp) && p.Port.Equals(item.Port), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A HL7 destination with the same, host/IP Address '{item.HostIp}' and port '{item.Port}' already exists."); + } + if (!item.IsValid(out var validationErrors)) + { + throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); + } + } + + private async Task ValidateUpdateAsync(HL7DestinationEntity item) + { + if (await _repository.ContainsAsync(p => !p.Name.Equals(item.Name) && p.HostIp.Equals(item.HostIp) && p.Port.Equals(item.Port), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A HL7 destination with the same, host/IP Address '{item.HostIp}' and port '{item.Port}' already exists."); + } + if (!item.IsValid(out var validationErrors)) + { + throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Http/HealthController.cs b/src/InformaticsGateway/Services/Http/HealthController.cs old mode 100644 new mode 100755 diff --git a/src/InformaticsGateway/Services/Http/Hl7ApplicationConfigController.cs b/src/InformaticsGateway/Services/Http/Hl7ApplicationConfigController.cs new file mode 100755 index 000000000..4eadd2f5c --- /dev/null +++ b/src/InformaticsGateway/Services/Http/Hl7ApplicationConfigController.cs @@ -0,0 +1,171 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.DicomWeb; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + [ApiController] + [Route("configEntity/hl7-application")] + public class Hl7ApplicationConfigController : ApiControllerBase + { + private const string Endpoint = "configEntity/hl7-application"; + + private readonly ILogger _logger; + private readonly IHl7ApplicationConfigRepository _repository; + + public Hl7ApplicationConfigController(ILogger logger, + IHl7ApplicationConfigRepository repository) + { + _logger = logger; + _repository = repository; + } + + [HttpGet] + [Produces(ContentTypes.ApplicationJson)] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task Get() + { + var data = await _repository.GetAllAsync().ConfigureAwait(false); + return Ok(data); + } + + [HttpGet("{id}")] + [Produces(ContentTypes.ApplicationJson)] + [ProducesResponseType(typeof(Hl7ApplicationConfigEntity), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get(string id) + { + var data = await _repository.GetByIdAsync(id).ConfigureAwait(false); + if (data == null) + { + return NotFound(); + } + + return Ok(data); + } + + [HttpDelete("{id}")] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task Delete(string id) + { + var data = await _repository.GetByIdAsync(id).ConfigureAwait(false); + if (data == null) + { + return NotFound(); + } + + try + { + var result = await _repository.DeleteAsync(id).ConfigureAwait(false); + return Ok(result); + } + catch (DatabaseException ex) + { + return Problem(title: "Database error removing HL7 Application Configuration.", statusCode: BadRequest, + detail: ex.Message); + } + catch (Exception ex) + { + return Problem(title: "Unknown error removing HL7 Application Configuration.", + statusCode: InternalServerError, detail: ex.Message); + } + } + + [HttpPut] + [Consumes(ContentTypes.ApplicationJson)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task Put([FromBody] Hl7ApplicationConfigEntity configEntity) + { + if (configEntity == null) + { + return Problem(title: "Hl7ApplicationConfigEntity is null.", statusCode: BadRequest, detail: "Hl7ApplicationConfigEntity is null."); + } + + var errorMessages = configEntity.Validate().ToList(); + if (errorMessages.Any()) + { + var message = "Hl7ApplicationConfigEntity is invalid. " + string.Join(", ", errorMessages); + return Problem(title: "Validation Failure.", statusCode: BadRequest, detail: message); + } + + try + { + await _repository.UpdateAsync(configEntity).ConfigureAwait(false); + return Ok(configEntity.Id); + } + catch (Exception ex) + { + _logger.PutHl7ApplicationConfigException(Endpoint, ex); + return Problem(title: "Error adding new HL7 Application Configuration.", + statusCode: InternalServerError, detail: ex.Message); + } + } + + [HttpPost] + [Consumes(ContentTypes.ApplicationJson)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task Post([FromBody] Hl7ApplicationConfigEntity configEntity) + { + if (configEntity == null) + { + return Problem(title: "Hl7ApplicationConfigEntity is null.", statusCode: BadRequest, detail: "Hl7ApplicationConfigEntity is null."); + } + + var errorMessages = configEntity.Validate().ToList(); + if (errorMessages.Any()) + { + var message = "Hl7ApplicationConfigEntity is invalid. " + string.Join(", ", errorMessages); + return Problem(title: "Validation Failure.", statusCode: BadRequest, detail: message); + } + + try + { + var result = await _repository.CreateAsync(configEntity).ConfigureAwait(false); + if (result is null) + { + return Problem(title: "Database error updating HL7 Application Configuration.", statusCode: BadRequest, + detail: "configEntity not found."); + } + return Ok(result); + } + catch (DatabaseException ex) + { + return Problem(title: "Database error updating HL7 Application Configuration.", statusCode: BadRequest, + detail: ex.Message); + } + catch (Exception ex) + { + return Problem(title: "Unknown error updating HL7 Application Configuration.", + statusCode: InternalServerError, detail: ex.Message); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Http/InferenceController.cs b/src/InformaticsGateway/Services/Http/InferenceController.cs index 1ce974a90..981031a69 100644 --- a/src/InformaticsGateway/Services/Http/InferenceController.cs +++ b/src/InformaticsGateway/Services/Http/InferenceController.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,7 +51,7 @@ public InferenceController( [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task JobStatus(string transactionId) { - Guard.Against.NullOrWhiteSpace(transactionId); + Guard.Against.NullOrWhiteSpace(transactionId, nameof(transactionId)); try { @@ -80,7 +80,7 @@ public async Task JobStatus(string transactionId) [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task NewInferenceRequest([FromBody] InferenceRequest request) { - Guard.Against.Null(request); + Guard.Against.Null(request, nameof(request)); if (!request.IsValid(out var details)) { @@ -90,12 +90,12 @@ public async Task NewInferenceRequest([FromBody] InferenceRequest using var _ = _logger.BeginScope(new LoggingDataDictionary { { "TransactionId", request.TransactionId } }); try { - if (await _inferenceRequestRepository.ExistsAsync(request.TransactionId, HttpContext.RequestAborted).ConfigureAwait(false)) { return Problem(title: "Conflict", statusCode: (int)HttpStatusCode.Conflict, detail: "An existing request with same transaction ID already exists."); } + request.CreatedBy = User.Identity?.Name; await _inferenceRequestRepository.AddAsync(request, HttpContext.RequestAborted).ConfigureAwait(false); } catch (Exception ex) diff --git a/src/InformaticsGateway/Services/Http/MonaiAeTitleController.cs b/src/InformaticsGateway/Services/Http/MonaiAeTitleController.cs old mode 100644 new mode 100755 index e3e69ac24..bc871763f --- a/src/InformaticsGateway/Services/Http/MonaiAeTitleController.cs +++ b/src/InformaticsGateway/Services/Http/MonaiAeTitleController.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.Mime; using System.Threading.Tasks; using Ardalis.GuardClauses; @@ -24,31 +23,29 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Scp; namespace Monai.Deploy.InformaticsGateway.Services.Http { [ApiController] [Route("config/ae")] - public class MonaiAeTitleController : ControllerBase + public class MonaiAeTitleController( + ILogger logger, + IMonaiAeChangedNotificationService monaiAeChangedNotificationService, + IMonaiApplicationEntityRepository repository, + IDataPlugInEngineFactory inputDataPlugInEngineFactory) : ControllerBase { - private readonly ILogger _logger; - private readonly IMonaiApplicationEntityRepository _repository; - private readonly IMonaiAeChangedNotificationService _monaiAeChangedNotificationService; - - public MonaiAeTitleController( - ILogger logger, - IMonaiAeChangedNotificationService monaiAeChangedNotificationService, - IMonaiApplicationEntityRepository repository) - { - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _repository = repository ?? throw new ArgumentNullException(nameof(repository)); - _monaiAeChangedNotificationService = monaiAeChangedNotificationService ?? throw new ArgumentNullException(nameof(monaiAeChangedNotificationService)); - } + private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + private readonly IMonaiApplicationEntityRepository _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + private readonly IDataPlugInEngineFactory _inputDataPlugInEngineFactory = inputDataPlugInEngineFactory ?? throw new ArgumentNullException(nameof(inputDataPlugInEngineFactory)); + private readonly IMonaiAeChangedNotificationService _monaiAeChangedNotificationService = monaiAeChangedNotificationService ?? throw new ArgumentNullException(nameof(monaiAeChangedNotificationService)); [HttpGet] [Produces("application/json")] @@ -104,9 +101,10 @@ public async Task> Create(MonaiApplicationE { try { - await ValidateAsync(item).ConfigureAwait(false); + await ValidateCreateAsync(item).ConfigureAwait(false); item.SetDefaultValues(); + item.SetAuthor(User, EditMode.Create); await _repository.AddAsync(item, HttpContext.RequestAborted).ConfigureAwait(false); _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent(item, ChangedEventType.Added)); @@ -128,6 +126,54 @@ public async Task> Create(MonaiApplicationE } } + [HttpPut] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Edit(MonaiApplicationEntity item) + { + try + { + if (item is null) + { + return NotFound(); + } + + var applicationEntity = await _repository.FindByNameAsync(item.Name, HttpContext.RequestAborted).ConfigureAwait(false); + if (applicationEntity is null) + { + return NotFound(); + } + + item.SetDefaultValues(); + + applicationEntity.AllowedSopClasses = item.AllowedSopClasses; + applicationEntity.Grouping = item.Grouping; + applicationEntity.Timeout = item.Timeout; + applicationEntity.IgnoredSopClasses = item.IgnoredSopClasses ?? []; + applicationEntity.Workflows = item.Workflows ?? []; + applicationEntity.PlugInAssemblies = item.PlugInAssemblies ?? []; + applicationEntity.SetAuthor(User, EditMode.Update); + + await ValidateUpdateAsync(applicationEntity).ConfigureAwait(false); + + _ = _repository.UpdateAsync(applicationEntity, HttpContext.RequestAborted); + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent(applicationEntity, ChangedEventType.Updated)); + _logger.MonaiApplicationEntityUpdated(item.Name, item.AeTitle); + return Ok(applicationEntity); + } + catch (ConfigurationException ex) + { + return Problem(title: "Validation error.", statusCode: (int)System.Net.HttpStatusCode.BadRequest, detail: ex.Message); + } + catch (Exception ex) + { + _logger.ErrorDeletingMonaiApplicationEntity(ex); + return Problem(title: "Error updating MONAI Application Entity.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + [HttpDelete("{name}")] [Produces("application/json")] [ProducesResponseType(StatusCodes.Status200OK)] @@ -156,9 +202,26 @@ public async Task> Delete(string name) } } - private async Task ValidateAsync(MonaiApplicationEntity item) + [HttpGet("plug-ins")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public ActionResult GetPlugIns() { - Guard.Against.Null(item); + try + { + return Ok(_inputDataPlugInEngineFactory.RegisteredPlugIns()); + } + catch (Exception ex) + { + _logger.ErrorReadingDataInputPlugIns(ex); + return Problem(title: "Error reading data input plug-ins.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + private async Task ValidateCreateAsync(MonaiApplicationEntity item) + { + Guard.Against.Null(item, nameof(item)); if (await _repository.ContainsAsync(p => p.Name.Equals(item.Name), HttpContext.RequestAborted).ConfigureAwait(false)) { @@ -168,7 +231,7 @@ private async Task ValidateAsync(MonaiApplicationEntity item) { throw new ObjectExistsException($"A MONAI Application Entity with the same AE Title '{item.AeTitle}' already exists."); } - if (item.IgnoredSopClasses.Any() && item.AllowedSopClasses.Any()) + if (item.IgnoredSopClasses.Count != 0 && item.AllowedSopClasses.Count is not 0) { throw new ConfigurationException($"Cannot specify both allowed and ignored SOP classes at the same time, they are mutually exclusive."); } @@ -177,5 +240,17 @@ private async Task ValidateAsync(MonaiApplicationEntity item) throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); } } + + private async Task ValidateUpdateAsync(MonaiApplicationEntity item) + { + if (await _repository.ContainsAsync(p => !p.Name.Equals(item.Name) && p.AeTitle.Equals(item.AeTitle), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A MONAI Application Entity with the same AE Title '{item.AeTitle}' already exists."); + } + if (!item.IsValid(out var validationErrors)) + { + throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); + } + } } } diff --git a/src/InformaticsGateway/Services/Http/MonaiHealthCheck.cs b/src/InformaticsGateway/Services/Http/MonaiHealthCheck.cs old mode 100644 new mode 100755 index 007995a2f..771c229de --- a/src/InformaticsGateway/Services/Http/MonaiHealthCheck.cs +++ b/src/InformaticsGateway/Services/Http/MonaiHealthCheck.cs @@ -18,7 +18,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Logging; using Monai.Deploy.InformaticsGateway.Repositories; namespace Monai.Deploy.InformaticsGateway.Services.Http @@ -26,10 +29,12 @@ namespace Monai.Deploy.InformaticsGateway.Services.Http public class MonaiHealthCheck : IHealthCheck { private readonly IMonaiServiceLocator _monaiServiceLocator; + private readonly ILogger _logger; - public MonaiHealthCheck(IMonaiServiceLocator monaiServiceLocator) + public MonaiHealthCheck(IMonaiServiceLocator monaiServiceLocator, ILogger logger) { _monaiServiceLocator = monaiServiceLocator ?? throw new ArgumentNullException(nameof(monaiServiceLocator)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) @@ -44,9 +49,10 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc if (unhealthyServices.Count == services.Count) { + _logger.AllServiceUnheathly(); return Task.FromResult(HealthCheckResult.Unhealthy(data: unhealthyServices)); } - + _logger.SomeServiceUnheathly(string.Join(",", unhealthyServices.Keys)); return Task.FromResult(HealthCheckResult.Degraded(data: unhealthyServices)); } } diff --git a/src/InformaticsGateway/Services/Http/PagedApiControllerBase.cs b/src/InformaticsGateway/Services/Http/PagedApiControllerBase.cs new file mode 100644 index 000000000..09db23ea7 --- /dev/null +++ b/src/InformaticsGateway/Services/Http/PagedApiControllerBase.cs @@ -0,0 +1,61 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using Ardalis.GuardClauses; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; +using Monai.Deploy.InformaticsGateway.Services.UriService; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + public class PagedApiControllerBase : ApiControllerBase + { + protected readonly IOptions EndpointOptions; + + public PagedApiControllerBase(IOptions options) + { + EndpointOptions = options ?? throw new ArgumentNullException(nameof(options)); + } + + /// + /// Creates a pagination paged response. + /// + /// Data set type. + /// Data set. + /// Filters. + /// Total records. + /// Uri service. + /// Route. + /// Returns . + public PagedResponse> CreatePagedResponse(IEnumerable pagedData, PaginationFilter validFilter, long totalRecords, IUriService uriService, string route) + { + Guard.Against.Null(pagedData, nameof(pagedData)); + Guard.Against.Null(validFilter, nameof(validFilter)); + Guard.Against.Null(route, nameof(route)); + Guard.Against.Null(uriService, nameof(uriService)); + + var pageSize = validFilter.PageSize ?? EndpointOptions.Value.DefaultPageSize; + var response = new PagedResponse>(pagedData, validFilter.PageNumber ?? 0, pageSize); + + response.SetUp(validFilter, totalRecords, uriService, route); + return response; + } + } +} diff --git a/src/InformaticsGateway/Services/Http/SourceAeTitleController.cs b/src/InformaticsGateway/Services/Http/SourceAeTitleController.cs index cd281ccda..ba926347e 100644 --- a/src/InformaticsGateway/Services/Http/SourceAeTitleController.cs +++ b/src/InformaticsGateway/Services/Http/SourceAeTitleController.cs @@ -88,6 +88,32 @@ public async Task> GetAeTitle(string name) } } + [HttpGet("aetitle/{aeTitle}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ActionName(nameof(GetAeTitleByAET))] + public async Task> GetAeTitleByAET(string aeTitle) + { + try + { + var sourceApplicationEntity = await _repository.FindByAETAsync(aeTitle, HttpContext.RequestAborted).ConfigureAwait(false); + + if (sourceApplicationEntity is null) + { + return NotFound(); + } + + return Ok(sourceApplicationEntity); + } + catch (Exception ex) + { + _logger.ErrorListingSourceApplicationEntities(ex); + return Problem(title: "Error querying DICOM sources.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + [HttpPost] [Consumes(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status201Created)] @@ -100,6 +126,7 @@ public async Task> Create(SourceApplicationEntity item) try { item.SetDefaultValues(); + item.SetAuthor(User, EditMode.Create); await ValidateCreateAsync(item).ConfigureAwait(false); await _repository.AddAsync(item, HttpContext.RequestAborted).ConfigureAwait(false); @@ -146,6 +173,7 @@ public async Task> Edit(SourceApplicationE sourceApplicationEntity.AeTitle = item.AeTitle; sourceApplicationEntity.HostIp = item.HostIp; + sourceApplicationEntity.SetAuthor(User, EditMode.Update); await ValidateEditAsync(sourceApplicationEntity).ConfigureAwait(false); diff --git a/src/InformaticsGateway/Services/Http/Startup.cs b/src/InformaticsGateway/Services/Http/Startup.cs old mode 100644 new mode 100755 index a510d91d8..bc72741c6 --- a/src/InformaticsGateway/Services/Http/Startup.cs +++ b/src/InformaticsGateway/Services/Http/Startup.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ using System.Net.Mime; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; using FellowOakDicom.Serialization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -28,8 +29,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; -using Monai.Deploy.InformaticsGateway.Database.EntityFramework; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Database; using Monai.Deploy.InformaticsGateway.Services.Fhir; +using Monai.Deploy.Security.Authentication.Extensions; namespace Monai.Deploy.InformaticsGateway.Services.Http { @@ -42,16 +45,15 @@ public Startup(IConfiguration configuration) public IConfiguration Configuration { get; } -#pragma warning disable CA1822 // Mark members as static public void ConfigureServices(IServiceCollection services) -#pragma warning restore CA1822 // Mark members as static { services.AddHttpContextAccessor(); + services.AddHttpLogging(o => { }); services.AddControllers(opts => { opts.RespectBrowserAcceptHeader = true; - var jsonSerializerOptions = new JsonSerializerOptions + var jsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNameCaseInsensitive = true, @@ -61,9 +63,9 @@ public void ConfigureServices(IServiceCollection services) WriteIndented = false }; + jsonSerializerOptions.TypeInfoResolver ??= JsonTypeInfoResolver.Combine(); jsonSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter(JsonNamingPolicy.CamelCase, false)); jsonSerializerOptions.Converters.Add(new DicomJsonConverter(writeTagsAsKeywords: false, autoValidate: false)); - jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false)); opts.OutputFormatters.Add(new FhirJsonFormatters(jsonSerializerOptions)); opts.OutputFormatters.Add(new FhirXmlFormatters()); }) @@ -75,9 +77,9 @@ public void ConfigureServices(IServiceCollection services) opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; opts.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString; opts.JsonSerializerOptions.WriteIndented = false; + opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter(new JsonStringEnumMemberConverterOptions(deserializationFailureFallbackValue: InferenceRequestType.Unknown), typeof(InferenceRequestType))); opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumMemberConverter(JsonNamingPolicy.CamelCase, false)); opts.JsonSerializerOptions.Converters.Add(new DicomJsonConverter(writeTagsAsKeywords: false, autoValidate: false)); - opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false)); }) .AddXmlSerializerFormatters(); @@ -89,6 +91,28 @@ public void ConfigureServices(IServiceCollection services) services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "MONAI Deploy Informatics Gateway", Version = "v1" }); + c.DescribeAllParametersInCamelCase(); + c.AddSecurityDefinition("basic", new OpenApiSecurityScheme + { + Scheme = "basic", + Name = "basic", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + }); + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "basic", + }, + }, + System.Array.Empty() + }, + }); }); services.Configure(options => @@ -106,9 +130,11 @@ public void ConfigureServices(IServiceCollection services) }; }); + services.AddMonaiAuthentication(); + services.AddHealthChecks() .AddCheck("Informatics Gateway Services") - .AddDbContextCheck("Database"); + .AddDatabaseHealthCheck(Configuration?.GetSection("ConnectionStrings")); } #pragma warning disable CA1822 // Mark members as static @@ -123,7 +149,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MONAI Deploy Informatics Gateway v1")); } - app.UseRouting(); app.UseHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions { ResponseWriter = async (context, report) => @@ -139,13 +164,20 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) }); context.Response.ContentType = MediaTypeNames.Application.Json; - await context.Response.WriteAsync(result); + await context.Response.WriteAsync(result).ConfigureAwait(false); } }); + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpointAuthorizationMiddleware(); + app.UseHttpLogging(); + app.UseEndpoints(endpoints => { - endpoints.MapControllers(); + endpoints.MapHealthChecks("/health").AllowAnonymous(); + endpoints.MapControllers().RequireAuthorization(); }); } } diff --git a/src/InformaticsGateway/Services/Http/VirtualAeTitleController.cs b/src/InformaticsGateway/Services/Http/VirtualAeTitleController.cs new file mode 100644 index 000000000..9c11d5084 --- /dev/null +++ b/src/InformaticsGateway/Services/Http/VirtualAeTitleController.cs @@ -0,0 +1,249 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Net.Mime; +using System.Threading.Tasks; +using System.Web; +using Ardalis.GuardClauses; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; + +namespace Monai.Deploy.InformaticsGateway.Services.Http +{ + [ApiController] + [Route("config/vae")] + public class VirtualAeTitleController : ControllerBase + { + private readonly ILogger _logger; + private readonly IVirtualApplicationEntityRepository _repository; + private readonly IDataPlugInEngineFactory _inputDataPlugInEngineFactory; + + public VirtualAeTitleController( + ILogger logger, + IVirtualApplicationEntityRepository repository, + IDataPlugInEngineFactory inputDataPlugInEngineFactory) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _repository = repository ?? throw new ArgumentNullException(nameof(repository)); + _inputDataPlugInEngineFactory = inputDataPlugInEngineFactory ?? throw new ArgumentNullException(nameof(inputDataPlugInEngineFactory)); + } + + [HttpGet] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task>> Get() + { + try + { + return Ok(await _repository.ToListAsync(HttpContext.RequestAborted).ConfigureAwait(false)); + } + catch (Exception ex) + { + _logger.ErrorQueryingDatabase(ex); + return Problem(title: "Error querying database.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + [HttpGet("{name}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ActionName(nameof(GetAeTitle))] + public async Task> GetAeTitle(string name) + { + try + { + var monaiApplicationEntity = await _repository.FindByNameAsync(name, HttpContext.RequestAborted).ConfigureAwait(false); + + if (monaiApplicationEntity is null) + { + return NotFound(); + } + + return Ok(monaiApplicationEntity); + } + catch (Exception ex) + { + _logger.ErrorListingMonaiApplicationEntities(ex); + return Problem(title: "Error querying Virtual Application Entity.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + [HttpPost] + [Consumes(MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status409Conflict)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [Produces("application/json")] + public async Task> Create(VirtualApplicationEntity item) + { + try + { + await ValidateCreateAsync(item).ConfigureAwait(false); + + item.SetDefaultValues(); + item.SetAuthor(User, EditMode.Create); + + await _repository.AddAsync(item, HttpContext.RequestAborted).ConfigureAwait(false); + _logger.VirtualApplicationEntityAdded(HttpUtility.HtmlEncode(item.VirtualAeTitle)); + return CreatedAtAction(nameof(GetAeTitle), new { name = item.Name }, item); + } + catch (ObjectExistsException ex) + { + return Problem(title: "AE Title already exists", statusCode: (int)System.Net.HttpStatusCode.Conflict, detail: ex.Message); + } + catch (ConfigurationException ex) + { + return Problem(title: "Validation error.", statusCode: (int)System.Net.HttpStatusCode.BadRequest, detail: ex.Message); + } + catch (Exception ex) + { + _logger.ErrorAddingVirtualApplicationEntity(ex); + return Problem(title: "Error adding new Virtual Application Entity.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + [HttpPut] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Edit(VirtualApplicationEntity item) + { + try + { + if (item is null) + { + return NotFound(); + } + + var applicationEntity = await _repository.FindByNameAsync(item.Name, HttpContext.RequestAborted).ConfigureAwait(false); + if (applicationEntity is null) + { + return NotFound(); + } + + item.SetDefaultValues(); + + applicationEntity.Workflows = item.Workflows ?? new List(); + applicationEntity.PlugInAssemblies = item.PlugInAssemblies ?? new List(); + applicationEntity.SetAuthor(User, EditMode.Update); + + await ValidateUpdateAsync(applicationEntity).ConfigureAwait(false); + + _ = _repository.UpdateAsync(applicationEntity, HttpContext.RequestAborted); + _logger.VirtualApplicationEntityUpdated(HttpUtility.HtmlEncode(item.Name), HttpUtility.HtmlEncode(item.VirtualAeTitle)); + return Ok(applicationEntity); + } + catch (ConfigurationException ex) + { + return Problem(title: "Validation error.", statusCode: (int)System.Net.HttpStatusCode.BadRequest, detail: ex.Message); + } + catch (Exception ex) + { + _logger.ErrorDeletingVirtualApplicationEntity(ex); + return Problem(title: "Error updating Virtual Application Entity.", statusCode: StatusCodes.Status500InternalServerError, detail: ex.Message); + } + } + + [HttpDelete("{name}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Delete(string name) + { + try + { + var monaiApplicationEntity = await _repository.FindByNameAsync(name, HttpContext.RequestAborted).ConfigureAwait(false); + if (monaiApplicationEntity is null) + { + return NotFound(); + } + + await _repository.RemoveAsync(monaiApplicationEntity, HttpContext.RequestAborted).ConfigureAwait(false); + + _logger.VirtualApplicationEntityDeleted(HttpUtility.HtmlEncode(name)); + return Ok(monaiApplicationEntity); + } + catch (Exception ex) + { + _logger.ErrorDeletingVirtualApplicationEntity(ex); + return Problem(title: "Error deleting Virtual Application Entity.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + [HttpGet("plug-ins")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public ActionResult GetPlugIns() + { + try + { + return Ok(_inputDataPlugInEngineFactory.RegisteredPlugIns()); + } + catch (Exception ex) + { + _logger.ErrorReadingDataInputPlugIns(ex); + return Problem(title: "Error reading data input plug-ins.", statusCode: (int)System.Net.HttpStatusCode.InternalServerError, detail: ex.Message); + } + } + + private async Task ValidateCreateAsync(VirtualApplicationEntity item) + { + Guard.Against.Null(item, nameof(item)); + + if (await _repository.ContainsAsync(p => p.Name.Equals(item.Name), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A Virtual Application Entity with the same name '{item.Name}' already exists."); + } + if (await _repository.ContainsAsync(p => p.VirtualAeTitle.Equals(item.VirtualAeTitle), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A Virtual Application Entity with the same AE Title '{item.VirtualAeTitle}' already exists."); + } + if (!item.IsValid(out var validationErrors)) + { + throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); + } + } + + private async Task ValidateUpdateAsync(VirtualApplicationEntity item) + { + if (await _repository.ContainsAsync(p => !p.Name.Equals(item.Name) && p.VirtualAeTitle.Equals(item.VirtualAeTitle), HttpContext.RequestAborted).ConfigureAwait(false)) + { + throw new ObjectExistsException($"A Virtual Application Entity with the same AE Title '{item.VirtualAeTitle}' already exists."); + } + if (!item.IsValid(out var validationErrors)) + { + throw new ConfigurationException(string.Join(Environment.NewLine, validationErrors)); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs b/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs old mode 100644 new mode 100755 index 52f803eee..7e3f2a9b3 --- a/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs +++ b/src/InformaticsGateway/Services/Scp/ApplicationEntityHandler.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,29 @@ using System.Linq; using System.Threading.Tasks; using Ardalis.GuardClauses; +using FellowOakDicom; using FellowOakDicom.Network; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Common; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Services.Scp { internal class ApplicationEntityHandler : IDisposable, IApplicationEntityHandler { + private readonly object _lock = new(); private readonly ILogger _logger; private readonly IOptions _options; @@ -42,7 +49,9 @@ internal class ApplicationEntityHandler : IDisposable, IApplicationEntityHandler private readonly IPayloadAssembler _payloadAssembler; private readonly IObjectUploadQueue _uploadQueue; private readonly IFileSystem _fileSystem; - private MonaiApplicationEntity _configuration; + private readonly IInputDataPlugInEngine _pluginEngine; + private readonly IExternalAppDetailsRepository _externalAppDetailsRepository; + private MonaiApplicationEntity? _configuration; private DicomJsonOptions _dicomJsonOptions; private bool _validateDicomValueOnJsonSerialization; private bool _disposedValue; @@ -50,9 +59,10 @@ internal class ApplicationEntityHandler : IDisposable, IApplicationEntityHandler public ApplicationEntityHandler( IServiceScopeFactory serviceScopeFactory, ILogger logger, - IOptions options) + IOptions options, + IExternalAppDetailsRepository externalAppDetailsRepository) { - Guard.Against.Null(serviceScopeFactory); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _options = options ?? throw new ArgumentNullException(nameof(options)); @@ -60,61 +70,133 @@ public ApplicationEntityHandler( _payloadAssembler = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IPayloadAssembler)); _uploadQueue = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IObjectUploadQueue)); _fileSystem = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IFileSystem)); + _pluginEngine = _serviceScope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IInputDataPlugInEngine)); + _externalAppDetailsRepository = externalAppDetailsRepository ?? throw new ServiceNotFoundException(nameof(externalAppDetailsRepository)); } public void Configure(MonaiApplicationEntity monaiApplicationEntity, DicomJsonOptions dicomJsonOptions, bool validateDicomValuesOnJsonSerialization) { - Guard.Against.Null(monaiApplicationEntity); + Guard.Against.Null(monaiApplicationEntity, nameof(monaiApplicationEntity)); - _configuration = monaiApplicationEntity; - _dicomJsonOptions = dicomJsonOptions; - _validateDicomValueOnJsonSerialization = validateDicomValuesOnJsonSerialization; + if (_configuration is not null && + (_configuration.Name != monaiApplicationEntity.Name || + _configuration.AeTitle != monaiApplicationEntity.AeTitle)) + { + throw new InvalidOperationException("Cannot update existing Application Entity Handler with different AE Title"); + } + + lock (_lock) + { + _configuration = monaiApplicationEntity; + _dicomJsonOptions = dicomJsonOptions; + _validateDicomValueOnJsonSerialization = validateDicomValuesOnJsonSerialization; + _pluginEngine.Configure(_configuration.PlugInAssemblies); + } } - public async Task HandleInstanceAsync(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, StudySerieSopUids uids) + public async Task HandleInstanceAsync(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, StudySerieSopUids uids, ScpInputTypeEnum type) { if (_configuration is null) { throw new NotSupportedException("Must call Configure(...) first."); } - Guard.Against.Null(request); - Guard.Against.NullOrWhiteSpace(calledAeTitle); - Guard.Against.NullOrWhiteSpace(callingAeTitle); - Guard.Against.Null(associationId); - Guard.Against.Null(uids); + Guard.Against.Null(request, nameof(request)); + Guard.Against.NullOrWhiteSpace(calledAeTitle, nameof(calledAeTitle)); + Guard.Against.NullOrWhiteSpace(callingAeTitle, nameof(callingAeTitle)); + Guard.Against.Null(associationId, nameof(associationId)); + Guard.Against.Null(uids, nameof(uids)); if (!AcceptsSopClass(uids.SopClassUid)) { _logger.InstanceIgnoredWIthMatchingSopClassUid(request.SOPClassUID.UID); - return; + return string.Empty; } + ExternalAppDetails? storedDetails; + + + + var dicomInfo = new DicomFileStorageMetadata( + associationId.ToString(), + uids.Identifier, + uids.StudyInstanceUid, + uids.SeriesInstanceUid, + uids.SopInstanceUid, + DataService.DIMSE, + callingAeTitle, + calledAeTitle); + - var dicomInfo = new DicomFileStorageMetadata(associationId.ToString(), uids.Identifier, uids.StudyInstanceUid, uids.SeriesInstanceUid, uids.SopInstanceUid) - { - CalledAeTitle = calledAeTitle, - Source = callingAeTitle, - }; if (_configuration.Workflows.Any()) { dicomInfo.SetWorkflows(_configuration.Workflows.ToArray()); } + if (request.File.Dataset.Contains(DicomTag.Modality)) + { + var modality = request.File.Dataset.GetSingleValue(DicomTag.Modality); - await dicomInfo.SetDataStreams(request.File, request.File.ToJson(_dicomJsonOptions, _validateDicomValueOnJsonSerialization), _options.Value.Storage.TemporaryDataStorage, _fileSystem, _options.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); - _uploadQueue.Queue(dicomInfo); + if (ArtifactTypes.Validate(modality) && Enum.TryParse(modality, out ArtifactType parsedArtifactType)) + { + dicomInfo.DataOrigin.ArtifactType = parsedArtifactType; + } + } + + var result = await _pluginEngine.ExecutePlugInsAsync(request.File, dicomInfo).ConfigureAwait(false); + + using var scope = _logger.BeginScope(new Api.LoggingDataDictionary() { { "CorrelationId", dicomInfo.CorrelationId } }); + + + if (type == ScpInputTypeEnum.ExternalAppReturn) + { + var uid = request.Dataset.GetSingleValue(DicomTag.StudyInstanceUID); + storedDetails = await _externalAppDetailsRepository + .GetByStudyIdOutboundAsync(uid, new System.Threading.CancellationToken()) + .ConfigureAwait(false); + if (storedDetails is null) + { + _logger.FailedToFindStoredExtAppDetails(uid); + } + RetrieveExternalAppDetails(storedDetails, dicomInfo); + dicomInfo.SetupGivenFilePaths(storedDetails?.DestinationFolder); + request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, storedDetails?.StudyInstanceUid); + request.Dataset.AddOrUpdate(DicomTag.PatientID, storedDetails?.PatientId); + } + + + dicomInfo = (result.Item2 as DicomFileStorageMetadata)!; + var dicomFile = result.Item1; + await dicomInfo.SetDataStreams(dicomFile, dicomFile.ToJson(_dicomJsonOptions, _validateDicomValueOnJsonSerialization), _options.Value.Storage.TemporaryDataStorage, _fileSystem, _options.Value.Storage.LocalTemporaryStoragePath).ConfigureAwait(false); var dicomTag = FellowOakDicom.DicomTag.Parse(_configuration.Grouping); _logger.QueueInstanceUsingDicomTag(dicomTag); - var key = request.Dataset.GetSingleValue(dicomTag); - await _payloadAssembler.Queue(key, dicomInfo, _configuration.Timeout).ConfigureAwait(false); + var key = dicomFile.Dataset.GetSingleValue(dicomTag); + + var payloadId = await _payloadAssembler.Queue(key, dicomInfo, dicomInfo.DataOrigin, _configuration.Timeout).ConfigureAwait(false); + dicomInfo.PayloadId = payloadId.ToString(); + _uploadQueue.Queue(dicomInfo); + + return dicomInfo.PayloadId; + } + + private void RetrieveExternalAppDetails(ExternalAppDetails? storedDetails, DicomFileStorageMetadata dicomInfo) + { + if (storedDetails is null) + { + return; + } + dicomInfo.ChangeCorrelationId(_logger, storedDetails.CorrelationId); + dicomInfo.WorkflowInstanceId = storedDetails.WorkflowInstanceId; + dicomInfo.TaskId = storedDetails.ExportTaskID; + dicomInfo.SetStudyInstanceUid(storedDetails.StudyInstanceUid); + //dicomInfo.DestinationFolderNeil = storedDetails.DestinationFolder!; } private bool AcceptsSopClass(string sopClassUid) { - Guard.Against.NullOrWhiteSpace(sopClassUid); + Guard.Against.NullOrWhiteSpace(sopClassUid, nameof(sopClassUid)); - if (_configuration.IgnoredSopClasses.Any()) + if (_configuration!.IgnoredSopClasses.Any()) { return !_configuration.IgnoredSopClasses.Contains(sopClassUid); } @@ -147,4 +229,4 @@ public void Dispose() GC.SuppressFinalize(this); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Services/Scp/ApplicationEntityManager.cs b/src/InformaticsGateway/Services/Scp/ApplicationEntityManager.cs old mode 100644 new mode 100755 index 4f7abe4ec..4e6aaf951 --- a/src/InformaticsGateway/Services/Scp/ApplicationEntityManager.cs +++ b/src/InformaticsGateway/Services/Scp/ApplicationEntityManager.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,10 +25,12 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Storage; namespace Monai.Deploy.InformaticsGateway.Services.Scp @@ -39,7 +41,6 @@ internal class ApplicationEntityManager : IApplicationEntityManager, IDisposable private readonly IServiceScope _serviceScope; private readonly ILogger _logger; private readonly IDicomToolkit _dicomToolkit; - private readonly ILoggerFactory _loggerFactory; private readonly IStorageInfoProvider _storageInfoProvider; private readonly ConcurrentDictionary _aeTitles; private readonly IDisposable _unsubscriberForMonaiAeChangedNotificationService; @@ -72,8 +73,8 @@ public ApplicationEntityManager(IHostApplicationLifetime applicationLifetime, _serviceScope = serviceScopeFactory.CreateScope(); var serviceProvider = _serviceScope.ServiceProvider; - _loggerFactory = serviceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ILoggerFactory)); - _logger = _loggerFactory.CreateLogger(); + var loggerFactory = serviceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(ILoggerFactory)); + _logger = loggerFactory.CreateLogger(); _dicomToolkit = serviceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IDicomToolkit)); _storageInfoProvider = serviceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageInfoProvider)); @@ -92,10 +93,9 @@ private void OnApplicationStopping() _unsubscriberForMonaiAeChangedNotificationService.Dispose(); } - - public async Task HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId) + public async Task HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, ScpInputTypeEnum type) { - Guard.Against.Null(request); + Guard.Against.Null(request, nameof(request)); await _initializeTask.ConfigureAwait(false); @@ -109,24 +109,30 @@ public async Task HandleCStoreRequest(DicomCStoreRequest request, string calledA throw new InsufficientStorageAvailableException($"Insufficient storage available. Available storage space: {_storageInfoProvider.AvailableFreeSpace:D}"); } - await HandleInstance(request, calledAeTitle, callingAeTitle, associationId).ConfigureAwait(false); + return await HandleInstance(request, calledAeTitle, callingAeTitle, associationId, type).ConfigureAwait(false); } - private async Task HandleInstance(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId) + private async Task HandleInstance(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, ScpInputTypeEnum type) { var uids = _dicomToolkit.GetStudySeriesSopInstanceUids(request.File); - using (_logger.BeginScope(new LoggingDataDictionary() { { "SOPInstanceUID", uids.SopInstanceUid }, { "CorrelationId", associationId } })) + using (_logger.BeginScope(new LoggingDataDictionary() { + { "SOPInstanceUID", uids.SopInstanceUid }, + { "CorrelationId", associationId }, + { "calledAeTitle", calledAeTitle}, + { "callingAeTitle",callingAeTitle}, + { "ThreadId", Environment.CurrentManagedThreadId} + })) { _logger.InstanceInformation(uids.StudyInstanceUid, uids.SeriesInstanceUid); - await _aeTitles[calledAeTitle].HandleInstanceAsync(request, calledAeTitle, callingAeTitle, associationId, uids).ConfigureAwait(false); + return await _aeTitles[calledAeTitle].HandleInstanceAsync(request, calledAeTitle, callingAeTitle, associationId, uids, type).ConfigureAwait(false); } } public async Task IsAeTitleConfiguredAsync(string calledAe) { - Guard.Against.NullOrWhiteSpace(calledAe); + Guard.Against.NullOrWhiteSpace(calledAe, nameof(calledAe)); await _initializeTask.ConfigureAwait(false); return _aeTitles.ContainsKey(calledAe); @@ -134,12 +140,7 @@ public async Task IsAeTitleConfiguredAsync(string calledAe) public T GetService() { - return (T)_serviceScope.ServiceProvider.GetService(typeof(T)); - } - - public ILogger GetLogger(string calledAeTitle) - { - return _loggerFactory.CreateLogger(calledAeTitle); + return (T)_serviceScope.ServiceProvider.GetService(typeof(T))!; } private async Task InitializeMonaiAeTitlesAsync() @@ -156,7 +157,7 @@ private async Task InitializeMonaiAeTitlesAsync() private void AddNewAeTitle(MonaiApplicationEntity entity) { - Guard.Against.Null(entity); + Guard.Against.Null(entity, nameof(entity)); var scope = _serviceScopeFactory.CreateScope(); var handler = scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IApplicationEntityHandler)); @@ -172,6 +173,25 @@ private void AddNewAeTitle(MonaiApplicationEntity entity) } } + private void UpdateAeTitle(MonaiApplicationEntity applicationEntity) + { + if (_aeTitles.TryGetValue(applicationEntity.AeTitle, out var aeHandler)) + { + try + { + aeHandler.Configure(applicationEntity, Configuration.Value.Dicom.WriteDicomJson, Configuration.Value.Dicom.ValidateDicomOnSerialization); + } + catch (Exception ex) + { + _logger.FailedToUpdateAppliationEntityHandlerWithUpdatedAEChange(applicationEntity.AeTitle, ex); + } + } + else + { + _logger.FailedToUpdateAppliationEntityHandlerWithUpdatedAEChange(applicationEntity.AeTitle); + } + } + public void OnCompleted() { // noop @@ -184,7 +204,7 @@ public void OnError(Exception error) public void OnNext(MonaiApplicationentityChangedEvent applicationChangedEvent) { - Guard.Against.Null(applicationChangedEvent); + Guard.Against.Null(applicationChangedEvent, nameof(applicationChangedEvent)); switch (applicationChangedEvent.Event) { @@ -192,6 +212,10 @@ public void OnNext(MonaiApplicationentityChangedEvent applicationChangedEvent) AddNewAeTitle(applicationChangedEvent.ApplicationEntity); break; + case ChangedEventType.Updated: + UpdateAeTitle(applicationChangedEvent.ApplicationEntity); + break; + case ChangedEventType.Deleted: _ = _aeTitles.TryRemove(applicationChangedEvent.ApplicationEntity.AeTitle, out _); _logger.AeTitleRemoved(applicationChangedEvent.ApplicationEntity.AeTitle); diff --git a/src/InformaticsGateway/Services/Scp/ExternalAppScpService.cs b/src/InformaticsGateway/Services/Scp/ExternalAppScpService.cs new file mode 100755 index 000000000..9d9551e53 --- /dev/null +++ b/src/InformaticsGateway/Services/Scp/ExternalAppScpService.cs @@ -0,0 +1,94 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using FellowOakDicom.Network; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Logging; + + +namespace Monai.Deploy.InformaticsGateway.Services.Scp +{ + internal class ExternalAppScpService : ScpServiceBase + { + private readonly IServiceScope _serviceScope; + private readonly ILogger _logger; + private readonly ILogger _scpServiceInternalLogger; + private readonly IOptions _configuration; + private readonly IApplicationEntityManager _associationDataProvider; + + public override string ServiceName => "External App DICOM SCP Service"; + + public ExternalAppScpService(IServiceScopeFactory serviceScopeFactory, + IApplicationEntityManager applicationEntityManager, + IHostApplicationLifetime appLifetime, + IOptions configuration) : base(serviceScopeFactory, applicationEntityManager, appLifetime, configuration) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(applicationEntityManager, nameof(applicationEntityManager)); + Guard.Against.Null(appLifetime, nameof(appLifetime)); + Guard.Against.Null(configuration, nameof(configuration)); + + _associationDataProvider = applicationEntityManager; + + _serviceScope = serviceScopeFactory.CreateScope(); + var logginFactory = _serviceScope.ServiceProvider.GetService(); + + _logger = logginFactory!.CreateLogger(); + _scpServiceInternalLogger = logginFactory!.CreateLogger(); + _configuration = configuration; + } + + public override void ServiceStart() + { + var ScpPort = _configuration.Value.Dicom.Scp.ExternalAppPort; + try + { + _logger.AddingScpListener(ServiceName, ScpPort); + Server = DicomServerFactory.Create( + NetworkManager.IPv4Any, + ScpPort, + logger: _scpServiceInternalLogger, + userState: _associationDataProvider, + configure: configure => configure.MaxClientsAllowed = _configuration.Value.Dicom.Scp.MaximumNumberOfAssociations); + + Server.Options.IgnoreUnsupportedTransferSyntaxChange = true; + Server.Options.LogDimseDatasets = _configuration.Value.Dicom.Scp.LogDimseDatasets; + + if (Server.Exception != null) + { + _logger.ScpListenerInitializationFailure(); + throw Server.Exception; + } + + Status = ServiceStatus.Running; + _logger.ScpListeningOnPort(ServiceName, ScpPort); + } + catch (System.Exception ex) + { + Status = ServiceStatus.Cancelled; + _logger.ServiceFailedToStart(ServiceName, ex); + AppLifetime.StopApplication(); + } + + } + } +} diff --git a/src/InformaticsGateway/Services/Scp/ExternalAppScpServiceInternal.cs b/src/InformaticsGateway/Services/Scp/ExternalAppScpServiceInternal.cs new file mode 100755 index 000000000..8a41f2c54 --- /dev/null +++ b/src/InformaticsGateway/Services/Scp/ExternalAppScpServiceInternal.cs @@ -0,0 +1,71 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Text; +using System.Threading.Tasks; +using FellowOakDicom.Network; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Logging; + + +namespace Monai.Deploy.InformaticsGateway.Services.Scp +{ + internal class ExternalAppScpServiceInternal : ScpServiceInternalBase + { + + private readonly DicomAssociationInfo _associationInfo; + private readonly ILogger _logger; + + public ExternalAppScpServiceInternal(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, DicomServiceDependencies dicomServiceDependencies) + : base(stream, fallbackEncoding, logger, dicomServiceDependencies) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _associationInfo = new DicomAssociationInfo(); + } + public override async Task OnCStoreRequestAsync(DicomCStoreRequest request) + { + try + { + _logger?.TransferSyntaxUsed(request.TransferSyntax); + var payloadId = await AssociationDataProvider!.HandleCStoreRequest(request, Association.CalledAE, Association.CallingAE, AssociationId, Common.ScpInputTypeEnum.ExternalAppReturn).ConfigureAwait(false); + _associationInfo.FileReceived(payloadId); + return new DicomCStoreResponse(request, DicomStatus.Success); + } + catch (InsufficientStorageAvailableException ex) + { + _logger?.CStoreFailedDueToLowStorageSpace(ex); + _associationInfo.Errors = $"Failed to store file due to low disk space: {ex}"; + return new DicomCStoreResponse(request, DicomStatus.ResourceLimitation); + } + catch (System.IO.IOException ex) when ((ex.HResult & 0xFFFF) == Constants.ERROR_HANDLE_DISK_FULL || (ex.HResult & 0xFFFF) == Constants.ERROR_DISK_FULL) + { + _logger?.CStoreFailedWithNoSpace(ex); + _associationInfo.Errors = $"Failed to store file due to low disk space: {ex}"; + return new DicomCStoreResponse(request, DicomStatus.StorageStorageOutOfResources); + } + catch (Exception ex) + { + _logger?.CStoreFailed(ex); + _associationInfo.Errors = $"Failed to store file: {ex}"; + return new DicomCStoreResponse(request, DicomStatus.ProcessingFailure); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Scp/IApplicationEntityHandler.cs b/src/InformaticsGateway/Services/Scp/IApplicationEntityHandler.cs old mode 100644 new mode 100755 index fe00e4ec8..f82e2d08e --- a/src/InformaticsGateway/Services/Scp/IApplicationEntityHandler.cs +++ b/src/InformaticsGateway/Services/Scp/IApplicationEntityHandler.cs @@ -17,9 +17,10 @@ using System; using System.Threading.Tasks; using FellowOakDicom.Network; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Services.Common; namespace Monai.Deploy.InformaticsGateway.Services.Scp { @@ -27,6 +28,6 @@ internal interface IApplicationEntityHandler { void Configure(MonaiApplicationEntity monaiApplicationEntity, DicomJsonOptions dicomJsonOptions, bool validateDicomValuesOnJsonSerialization); - Task HandleInstanceAsync(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, StudySerieSopUids uids); + Task HandleInstanceAsync(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, StudySerieSopUids uids, ScpInputTypeEnum type); } } diff --git a/src/InformaticsGateway/Services/Scp/IApplicationEntityManager.cs b/src/InformaticsGateway/Services/Scp/IApplicationEntityManager.cs old mode 100644 new mode 100755 index 5681e98fc..897ab9eb2 --- a/src/InformaticsGateway/Services/Scp/IApplicationEntityManager.cs +++ b/src/InformaticsGateway/Services/Scp/IApplicationEntityManager.cs @@ -18,9 +18,9 @@ using System; using System.Threading.Tasks; using FellowOakDicom.Network; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Services.Common; namespace Monai.Deploy.InformaticsGateway.Services.Scp { @@ -32,11 +32,12 @@ public interface IApplicationEntityManager /// /// Handles the C-Store request. /// - /// Instance of . + /// Instance of . /// Called AE Title to be associated with the call. - /// Calling AE Title to be associated with the call. + /// Calling AE Title to be associated with the call. /// Unique association ID. - Task HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId); + /// SCP input type. + Task HandleCStoreRequest(DicomCStoreRequest request, string calledAeTitle, string callingAeTitle, Guid associationId, ScpInputTypeEnum type = ScpInputTypeEnum.WorkflowTrigger); /// /// Checks if a MONAI AET is configured. @@ -51,15 +52,11 @@ public interface IApplicationEntityManager /// T GetService(); - /// - /// Wrapper to get a typed logger. - /// - ILogger GetLogger(string calledAeTitle); - /// /// Checks if source AE Title is configured. /// /// + /// /// Task IsValidSourceAsync(string callingAe, string host); } diff --git a/src/InformaticsGateway/Services/Scp/IMonaiAeChangedNotificationService.cs b/src/InformaticsGateway/Services/Scp/IMonaiAeChangedNotificationService.cs old mode 100644 new mode 100755 index 2de4fccb8..adf544c61 --- a/src/InformaticsGateway/Services/Scp/IMonaiAeChangedNotificationService.cs +++ b/src/InformaticsGateway/Services/Scp/IMonaiAeChangedNotificationService.cs @@ -16,7 +16,7 @@ */ using System; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; namespace Monai.Deploy.InformaticsGateway.Services.Scp { diff --git a/src/InformaticsGateway/Services/Scp/MonaiAeChangedNotificationService.cs b/src/InformaticsGateway/Services/Scp/MonaiAeChangedNotificationService.cs index daa4650a8..79669794b 100644 --- a/src/InformaticsGateway/Services/Scp/MonaiAeChangedNotificationService.cs +++ b/src/InformaticsGateway/Services/Scp/MonaiAeChangedNotificationService.cs @@ -38,7 +38,7 @@ public MonaiAeChangedNotificationService(ILogger observer) { - Guard.Against.Null(observer); + Guard.Against.Null(observer, nameof(observer)); if (!_observers.Contains(observer)) { @@ -50,7 +50,7 @@ public IDisposable Subscribe(IObserver obser public void Notify(MonaiApplicationentityChangedEvent monaiApplicationChangedEvent) { - Guard.Against.Null(monaiApplicationChangedEvent); + Guard.Against.Null(monaiApplicationChangedEvent, nameof(monaiApplicationChangedEvent)); _logger.NotifyAeChanged(_observers.Count, monaiApplicationChangedEvent.Event); diff --git a/src/InformaticsGateway/Services/Scp/ScpService.cs b/src/InformaticsGateway/Services/Scp/ScpService.cs old mode 100644 new mode 100755 index 3d015e607..85c755b4a --- a/src/InformaticsGateway/Services/Scp/ScpService.cs +++ b/src/InformaticsGateway/Services/Scp/ScpService.cs @@ -1,6 +1,5 @@ -/* - * Copyright 2021-2022 MONAI Consortium - * Copyright 2019-2021 NVIDIA Corporation +/* + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,108 +14,45 @@ * limitations under the License. */ -using System; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using Ardalis.GuardClauses; -using FellowOakDicom; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.InformaticsGateway.Services.Common; -using FoDicomNetwork = FellowOakDicom.Network; namespace Monai.Deploy.InformaticsGateway.Services.Scp { - internal sealed class ScpService : IHostedService, IDisposable, IMonaiService + internal class ScpService : ScpServiceBase { -#pragma warning disable S2223 // Non-constant static fields should not be visible - public static int ActiveConnections = 0; -#pragma warning restore S2223 // Non-constant static fields should not be visible - private readonly IServiceScope _serviceScope; - private readonly IApplicationEntityManager _associationDataProvider; private readonly ILogger _logger; - private readonly IHostApplicationLifetime _appLifetime; private readonly IOptions _configuration; - private FoDicomNetwork.IDicomServer _server; - public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; - public string ServiceName => "DICOM SCP Service"; + + public override string ServiceName => "DICOM SCP Service"; public ScpService(IServiceScopeFactory serviceScopeFactory, IApplicationEntityManager applicationEntityManager, IHostApplicationLifetime appLifetime, - IOptions configuration) + IOptions configuration) : base(serviceScopeFactory, applicationEntityManager, appLifetime, configuration) { - Guard.Against.Null(serviceScopeFactory); - Guard.Against.Null(applicationEntityManager); - Guard.Against.Null(appLifetime); - Guard.Against.Null(configuration); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(applicationEntityManager, nameof(applicationEntityManager)); + Guard.Against.Null(appLifetime, nameof(appLifetime)); + Guard.Against.Null(configuration, nameof(configuration)); _serviceScope = serviceScopeFactory.CreateScope(); - _associationDataProvider = applicationEntityManager; - var logginFactory = _serviceScope.ServiceProvider.GetService(); - _logger = logginFactory.CreateLogger(); - _appLifetime = appLifetime; + _logger = logginFactory!.CreateLogger(); _configuration = configuration; - _ = DicomDictionary.Default; - } - - public void Dispose() - { - _serviceScope.Dispose(); - _server?.Dispose(); - GC.SuppressFinalize(this); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - _logger.ScpServiceLoading(_configuration.Value.Dicom.Scp.Port); - - try - { - _logger.ServiceStarting(ServiceName); - _server = FoDicomNetwork.DicomServerFactory.Create( - FoDicomNetwork.NetworkManager.IPv4Any, - _configuration.Value.Dicom.Scp.Port, - userState: _associationDataProvider); - - _server.Options.IgnoreUnsupportedTransferSyntaxChange = true; - _server.Options.LogDimseDatasets = _configuration.Value.Dicom.Scp.LogDimseDatasets; - _server.Options.MaxClientsAllowed = _configuration.Value.Dicom.Scp.MaximumNumberOfAssociations; - - if (_server.Exception != null) - { - _logger.ScpListenerInitializationFailure(); - throw _server.Exception; - } - - Status = ServiceStatus.Running; - _logger.ScpListeningOnPort(_configuration.Value.Dicom.Scp.Port); - } - catch (System.Exception ex) - { - Status = ServiceStatus.Cancelled; - _logger.ServiceFailedToStart(ServiceName, ex); - _appLifetime.StopApplication(); - } - return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) + public override void ServiceStart() { - _logger.ServiceStopping(ServiceName); - _server?.Stop(); - _server?.Dispose(); - Status = ServiceStatus.Stopped; - return Task.CompletedTask; + _logger.AddingScpListener(ServiceName, _configuration.Value.Dicom.Scp.Port); + ServiceStartBase(_configuration.Value.Dicom.Scp.Port); } } } diff --git a/src/InformaticsGateway/Services/Scp/ScpServiceBase.cs b/src/InformaticsGateway/Services/Scp/ScpServiceBase.cs new file mode 100755 index 000000000..67b1b63ac --- /dev/null +++ b/src/InformaticsGateway/Services/Scp/ScpServiceBase.cs @@ -0,0 +1,131 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; +using FellowOakDicom; +using FellowOakDicom.Network; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Rest; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Logging; +using Monai.Deploy.InformaticsGateway.Services.Common; + +using FoDicomNetwork = FellowOakDicom.Network; + +namespace Monai.Deploy.InformaticsGateway.Services.Scp +{ + internal abstract class ScpServiceBase : IHostedService, IDisposable, IMonaiService + { +#pragma warning disable S2223 // Non-constant static fields should not be visible + public static int ActiveConnections = 0; +#pragma warning restore S2223 // Non-constant static fields should not be visible + + private readonly IServiceScope _serviceScope; + private readonly IApplicationEntityManager _associationDataProvider; + private readonly ILogger _logger; + private readonly ILogger _scpServiceInternalLogger; + protected readonly IHostApplicationLifetime AppLifetime; + private readonly IOptions _configuration; + protected FoDicomNetwork.IDicomServer? Server; + public ServiceStatus Status { get; set; } = ServiceStatus.Unknown; + public abstract string ServiceName { get; } + + public ScpServiceBase(IServiceScopeFactory serviceScopeFactory, + IApplicationEntityManager applicationEntityManager, + IHostApplicationLifetime appLifetime, + IOptions configuration) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(applicationEntityManager, nameof(applicationEntityManager)); + Guard.Against.Null(appLifetime, nameof(appLifetime)); + Guard.Against.Null(configuration, nameof(configuration)); + + _serviceScope = serviceScopeFactory.CreateScope(); + + var logginFactory = _serviceScope.ServiceProvider.GetService(); + + _logger = logginFactory!.CreateLogger(); + _scpServiceInternalLogger = logginFactory!.CreateLogger(); + _associationDataProvider = applicationEntityManager; + AppLifetime = appLifetime; + _configuration = configuration; + _ = DicomDictionary.Default; + } + + public void Dispose() + { + _serviceScope.Dispose(); + Server?.Dispose(); + GC.SuppressFinalize(this); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + ServiceStart(); + return Task.CompletedTask; + } + + public abstract void ServiceStart(); + + public Task StopAsync(CancellationToken cancellationToken) + { + _logger.ServiceStopping(ServiceName); + Server?.Stop(); + Server?.Dispose(); + Status = ServiceStatus.Stopped; + return Task.CompletedTask; + } + + public void ServiceStartBase(int ScpPort) + { + try + { + _logger.ServiceStarting(ServiceName); + Server = DicomServerFactory.Create( + NetworkManager.IPv4Any, + ScpPort, + logger: _scpServiceInternalLogger, + userState: _associationDataProvider, + configure: configure => configure.MaxClientsAllowed = _configuration.Value.Dicom.Scp.MaximumNumberOfAssociations); + + Server.Options.IgnoreUnsupportedTransferSyntaxChange = true; + Server.Options.LogDimseDatasets = _configuration.Value.Dicom.Scp.LogDimseDatasets; + + if (Server.Exception != null) + { + _logger.ScpListenerInitializationFailure(); + throw Server.Exception; + } + + Status = ServiceStatus.Running; + _logger.ScpListeningOnPort(ServiceName, ScpPort); + } + catch (System.Exception ex) + { + Status = ServiceStatus.Cancelled; + _logger.ServiceFailedToStart(ServiceName, ex); + AppLifetime.StopApplication(); + } + } + } +} diff --git a/src/InformaticsGateway/Services/Scp/ScpServiceInternal.cs b/src/InformaticsGateway/Services/Scp/ScpServiceInternal.cs old mode 100644 new mode 100755 index 841b87c94..a19ef3825 --- a/src/InformaticsGateway/Services/Scp/ScpServiceInternal.cs +++ b/src/InformaticsGateway/Services/Scp/ScpServiceInternal.cs @@ -1,5 +1,5 @@ -/* - * Copyright 2021-2022 MONAI Consortium +/* + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,184 +16,61 @@ */ using System; -using System.Linq; using System.Text; -using System.Threading; using System.Threading.Tasks; -using FellowOakDicom; using FellowOakDicom.Network; -using Monai.Deploy.InformaticsGateway.Api; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Logging; namespace Monai.Deploy.InformaticsGateway.Services.Scp { - /// - /// A new instance of ScpServiceInternal is created for every new association. - /// - internal class ScpServiceInternal : - DicomService, - IDicomServiceProvider, - IDicomCEchoProvider, - IDicomCStoreProvider + internal class ScpServiceInternal : ScpServiceInternalBase { - private Microsoft.Extensions.Logging.ILogger _logger; - private IApplicationEntityManager _associationDataProvider; - private IDisposable _loggerScope; - private Guid _associationId; - private DateTimeOffset? _associationReceived; - public ScpServiceInternal(INetworkStream stream, Encoding fallbackEncoding, FellowOakDicom.Log.ILogger log, DicomServiceDependencies dicomServiceDependencies) - : base(stream, fallbackEncoding, log, dicomServiceDependencies) - { - } - public Task OnCEchoRequestAsync(DicomCEchoRequest request) - { - _logger?.CEchoReceived(); - return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success)); - } + private readonly DicomAssociationInfo _associationInfo; + private readonly ILogger _logger; + //private IApplicationEntityManager? _associationDataProvider; + //private IDisposable? _loggerScope; + //private Guid _associationId; + //private DateTimeOffset? _associationReceived; - public void OnConnectionClosed(Exception exception) + public ScpServiceInternal(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, DicomServiceDependencies dicomServiceDependencies) + : base(stream, fallbackEncoding, logger, dicomServiceDependencies) { - if (exception != null) - { - _logger?.ConnectionClosedWithException(exception); - } - - _loggerScope?.Dispose(); - Interlocked.Decrement(ref ScpService.ActiveConnections); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _associationInfo = new DicomAssociationInfo(); } - - public async Task OnCStoreRequestAsync(DicomCStoreRequest request) + public override async Task OnCStoreRequestAsync(DicomCStoreRequest request) { try { _logger?.TransferSyntaxUsed(request.TransferSyntax); - await _associationDataProvider.HandleCStoreRequest(request, Association.CalledAE, Association.CallingAE, _associationId).ConfigureAwait(false); + var payloadId = await AssociationDataProvider!.HandleCStoreRequest(request, Association.CalledAE, Association.CallingAE, AssociationId, Common.ScpInputTypeEnum.WorkflowTrigger).ConfigureAwait(false); + _associationInfo.FileReceived(payloadId); return new DicomCStoreResponse(request, DicomStatus.Success); } catch (InsufficientStorageAvailableException ex) { _logger?.CStoreFailedDueToLowStorageSpace(ex); + _associationInfo.Errors = $"Failed to store file due to low disk space: {ex}"; return new DicomCStoreResponse(request, DicomStatus.ResourceLimitation); } catch (System.IO.IOException ex) when ((ex.HResult & 0xFFFF) == Constants.ERROR_HANDLE_DISK_FULL || (ex.HResult & 0xFFFF) == Constants.ERROR_DISK_FULL) { _logger?.CStoreFailedWithNoSpace(ex); + _associationInfo.Errors = $"Failed to store file due to low disk space: {ex}"; return new DicomCStoreResponse(request, DicomStatus.StorageStorageOutOfResources); } catch (Exception ex) { _logger?.CStoreFailed(ex); + _associationInfo.Errors = $"Failed to store file: {ex}"; return new DicomCStoreResponse(request, DicomStatus.ProcessingFailure); } } - public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e) - { - _logger?.CStoreFailed(e); - return Task.CompletedTask; - } - - public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) - { - _logger?.CStoreAbort(source, reason); - } - - /// - /// Start timer only if a receive association release request is received. - /// - /// - public Task OnReceiveAssociationReleaseRequestAsync() - { - var associationElapsed = TimeSpan.Zero; - if (_associationReceived.HasValue) - { - associationElapsed = DateTimeOffset.UtcNow.Subtract(_associationReceived.Value); - } - - _logger?.CStoreAssociationReleaseRequest(associationElapsed); - return SendAssociationReleaseResponseAsync(); - } - - public async Task OnReceiveAssociationRequestAsync(DicomAssociation association) - { - Interlocked.Increment(ref ScpService.ActiveConnections); - _associationReceived = DateTimeOffset.UtcNow; - _associationDataProvider = UserState as IApplicationEntityManager; - - if (_associationDataProvider is null) - { - throw new ServiceException($"{nameof(UserState)} must be an instance of IAssociationDataProvider"); - } - - _logger = _associationDataProvider.GetLogger(Association.CalledAE); - - _associationId = Guid.NewGuid(); - var associationIdStr = $"#{_associationId} {association.RemoteHost}:{association.RemotePort}"; - - _loggerScope = _logger?.BeginScope(new LoggingDataDictionary { { "Association", associationIdStr } }); - _logger?.CStoreAssociationReceived(association.RemoteHost, association.RemotePort); - - if (!await IsValidSourceAeAsync(association.CallingAE, association.RemoteHost).ConfigureAwait(false)) - { - await SendAssociationRejectAsync( - DicomRejectResult.Permanent, - DicomRejectSource.ServiceUser, - DicomRejectReason.CallingAENotRecognized).ConfigureAwait(false); - } - - if (!await IsValidCalledAeAsync(association.CalledAE).ConfigureAwait(false)) - { - await SendAssociationRejectAsync( - DicomRejectResult.Permanent, - DicomRejectSource.ServiceUser, - DicomRejectReason.CalledAENotRecognized).ConfigureAwait(false); - } - - foreach (var pc in association.PresentationContexts) - { - if (pc.AbstractSyntax == DicomUID.Verification) - { - if (!_associationDataProvider.Configuration.Value.Dicom.Scp.EnableVerification) - { - _logger?.VerificationServiceDisabled(); - await SendAssociationRejectAsync( - DicomRejectResult.Permanent, - DicomRejectSource.ServiceUser, - DicomRejectReason.ApplicationContextNotSupported - ).ConfigureAwait(false); - } - pc.AcceptTransferSyntaxes(_associationDataProvider.Configuration.Value.Dicom.Scp.VerificationServiceTransferSyntaxes.ToDicomTransferSyntaxArray()); - } - else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None) - { - if (!_associationDataProvider.CanStore) - { - await SendAssociationRejectAsync( - DicomRejectResult.Permanent, - DicomRejectSource.ServiceUser, - DicomRejectReason.NoReasonGiven).ConfigureAwait(false); - } - // Accept any proposed TS - pc.AcceptTransferSyntaxes(pc.GetTransferSyntaxes().ToArray()); - } - } - - await SendAssociationAcceptAsync(association).ConfigureAwait(false); - } - - private async Task IsValidCalledAeAsync(string calledAe) - { - return await _associationDataProvider.IsAeTitleConfiguredAsync(calledAe).ConfigureAwait(false); - } - - private async Task IsValidSourceAeAsync(string callingAe, string host) - { - if (!_associationDataProvider.Configuration.Value.Dicom.Scp.RejectUnknownSources) return true; - - return await _associationDataProvider.IsValidSourceAsync(callingAe, host); - } } } diff --git a/src/InformaticsGateway/Services/Scp/ScpServiceInternalBase.cs b/src/InformaticsGateway/Services/Scp/ScpServiceInternalBase.cs new file mode 100755 index 000000000..3548aaaf4 --- /dev/null +++ b/src/InformaticsGateway/Services/Scp/ScpServiceInternalBase.cs @@ -0,0 +1,207 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using FellowOakDicom; +using FellowOakDicom.Network; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Logging; + +namespace Monai.Deploy.InformaticsGateway.Services.Scp +{ + /// + /// A new instance of ScpServiceInternal is created for every new association. + /// + internal abstract class ScpServiceInternalBase : + DicomService, + IDicomServiceProvider, + IDicomCEchoProvider, + IDicomCStoreProvider + { + private readonly DicomAssociationInfo _associationInfo; + private readonly ILogger _logger; + protected IApplicationEntityManager? AssociationDataProvider; + private IDisposable? _loggerScope; + protected Guid AssociationId; + private DateTimeOffset? _associationReceived; + + public ScpServiceInternalBase(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, DicomServiceDependencies dicomServiceDependencies) + : base(stream, fallbackEncoding, logger, dicomServiceDependencies) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _associationInfo = new DicomAssociationInfo(); + } + + public Task OnCEchoRequestAsync(DicomCEchoRequest request) + { + _logger?.CEchoReceived(); + return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success)); + } + + public void OnConnectionClosed(Exception exception) + { + if (exception != null) + { + _logger?.ConnectionClosedWithException(exception); + } + + _loggerScope?.Dispose(); + Interlocked.Decrement(ref ScpServiceBase.ActiveConnections); + + try + { + var repo = AssociationDataProvider!.GetService(); + _associationInfo.Disconnect(); + repo?.AddAsync(_associationInfo).Wait(); + _logger?.ConnectionClosed(_associationInfo.CorrelationId, _associationInfo.CallingAeTitle, _associationInfo.CalledAeTitle, _associationInfo.Duration.TotalSeconds); + } + catch (Exception ex) + { + _logger?.ErrorSavingDicomAssociationInfo(AssociationId, ex); + } + } + + public abstract Task OnCStoreRequestAsync(DicomCStoreRequest request); + + public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e) + { + _logger?.CStoreFailed(e); + _associationInfo.Errors = $"Failed to store file: {e}"; + return Task.CompletedTask; + } + + public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) + { + _logger?.CStoreAbort(source, reason); + _associationInfo.Errors = $"{source} - {reason}"; + } + + /// + /// Start timer only if a receive association release request is received. + /// + /// + public Task OnReceiveAssociationReleaseRequestAsync() + { + var associationElapsed = TimeSpan.Zero; + if (_associationReceived.HasValue) + { + associationElapsed = DateTimeOffset.UtcNow.Subtract(_associationReceived.Value); + } + + _logger?.CStoreAssociationReleaseRequest(associationElapsed); + return SendAssociationReleaseResponseAsync(); + } + + public async Task OnReceiveAssociationRequestAsync(DicomAssociation association) + { + Interlocked.Increment(ref ScpServiceBase.ActiveConnections); + _associationReceived = DateTimeOffset.UtcNow; + AssociationDataProvider = (UserState as IApplicationEntityManager)!; + + if (AssociationDataProvider is null) + { + _associationInfo.Errors = $"Internal error: association data provider not found."; + throw new ServiceException($"{nameof(UserState)} must be an instance of IAssociationDataProvider"); + } + + AssociationId = Guid.NewGuid(); + var associationIdStr = $"#{AssociationId} {association.RemoteHost}:{association.RemotePort}"; + + _loggerScope = _logger!.BeginScope(new LoggingDataDictionary { { "Association", associationIdStr } }); + _logger.CStoreAssociationReceived(association.RemoteHost, association.RemotePort); + + _associationInfo.CallingAeTitle = association.CallingAE; + _associationInfo.CalledAeTitle = association.CalledAE; + _associationInfo.RemoteHost = association.RemoteHost; + _associationInfo.RemotePort = association.RemotePort; + _associationInfo.CorrelationId = AssociationId.ToString(); + + if (!await IsValidSourceAeAsync(association.CallingAE, association.RemoteHost).ConfigureAwait(false)) + { + _associationInfo.Errors = $"Invalid source. Called AE: {association.CalledAE}. Calling AE: {association.CallingAE}. IP: {association.RemoteHost}."; + + await SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.CallingAENotRecognized).ConfigureAwait(false); + } + + if (!await IsValidCalledAeAsync(association.CalledAE).ConfigureAwait(false)) + { + _associationInfo.Errors = "Invalid MONAI AE Title. Called AE: {association.CalledAE}. Calling AE: {association.CallingAE}. IP: {association.RemoteHost}."; + + await SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.CalledAENotRecognized).ConfigureAwait(false); + } + + foreach (var pc in association.PresentationContexts) + { + if (pc.AbstractSyntax == DicomUID.Verification) + { + if (!AssociationDataProvider.Configuration.Value.Dicom.Scp.EnableVerification) + { + _associationInfo.Errors = "Verification service disabled. Called AE: {association.CalledAE}. Calling AE: {association.CallingAE}. IP: {association.RemoteHost}."; + _logger?.VerificationServiceDisabled(); + await SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.ApplicationContextNotSupported + ).ConfigureAwait(false); + } + pc.AcceptTransferSyntaxes(AssociationDataProvider.Configuration.Value.Dicom.Scp.VerificationServiceTransferSyntaxes.ToDicomTransferSyntaxArray()); + } + else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None) + { + if (!AssociationDataProvider.CanStore) + { + _associationInfo.Errors = "Disk pressure. Called AE: {association.CalledAE}. Calling AE: {association.CallingAE}. IP: {association.RemoteHost}."; + await SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.NoReasonGiven).ConfigureAwait(false); + } + // Accept any proposed TS + pc.AcceptTransferSyntaxes(pc.GetTransferSyntaxes().ToArray()); + } + } + + await SendAssociationAcceptAsync(association).ConfigureAwait(false); + } + + private async Task IsValidCalledAeAsync(string calledAe) + { + return await AssociationDataProvider!.IsAeTitleConfiguredAsync(calledAe).ConfigureAwait(false); + } + + private async Task IsValidSourceAeAsync(string callingAe, string host) + { + if (!AssociationDataProvider!.Configuration.Value.Dicom.Scp.RejectUnknownSources) return true; + + return await AssociationDataProvider.IsValidSourceAsync(callingAe, host).ConfigureAwait(false); + } + } +} diff --git a/src/InformaticsGateway/Services/Scu/ScuQueue.cs b/src/InformaticsGateway/Services/Scu/ScuQueue.cs old mode 100644 new mode 100755 index 8213931b4..a59e0eba7 --- a/src/InformaticsGateway/Services/Scu/ScuQueue.cs +++ b/src/InformaticsGateway/Services/Scu/ScuQueue.cs @@ -14,7 +14,6 @@ * limitations under the License. */ -using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; @@ -26,12 +25,10 @@ namespace Monai.Deploy.InformaticsGateway.Services.Scu internal class ScuQueue : IScuQueue { private readonly BlockingCollection _workItems; - private readonly ILogger _logger; public ScuQueue(ILogger logger) { _workItems = new BlockingCollection(); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public ScuWorkRequest Dequeue(CancellationToken cancellationToken) @@ -41,7 +38,7 @@ public ScuWorkRequest Dequeue(CancellationToken cancellationToken) public async Task Queue(ScuWorkRequest request, CancellationToken cancellationToken) { - Guard.Against.Null(request); + Guard.Against.Null(request, nameof(request)); _workItems.Add(request, cancellationToken); return await request.WaitAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/InformaticsGateway/Services/Scu/ScuService.cs b/src/InformaticsGateway/Services/Scu/ScuService.cs old mode 100644 new mode 100755 index 5154139f8..4326d0535 --- a/src/InformaticsGateway/Services/Scu/ScuService.cs +++ b/src/InformaticsGateway/Services/Scu/ScuService.cs @@ -47,7 +47,7 @@ public ScuService(IServiceScopeFactory serviceScopeFactory, ILogger logger, IOptions configuration) { - Guard.Against.Null(serviceScopeFactory); + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); _scope = serviceScopeFactory.CreateScope(); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -56,7 +56,7 @@ public ScuService(IServiceScopeFactory serviceScopeFactory, _workQueue = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IScuQueue)); } - private async Task BackgroundProcessingAsync(CancellationToken cancellationToken) + private Task BackgroundProcessing(CancellationToken cancellationToken) { _logger.ServiceRunning(ServiceName); while (!cancellationToken.IsCancellationRequested) @@ -64,7 +64,10 @@ private async Task BackgroundProcessingAsync(CancellationToken cancellationToken try { var item = _workQueue.Dequeue(cancellationToken); - await Process(item, cancellationToken).ConfigureAwait(false); + + using var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, item.CancellationToken); + + ProcessThread(item, linkedCancellationToken.Token); } catch (ObjectDisposedException ex) { @@ -80,11 +83,17 @@ private async Task BackgroundProcessingAsync(CancellationToken cancellationToken } Status = ServiceStatus.Cancelled; _logger.ServiceCancelled(ServiceName); + return Task.CompletedTask; + } + + private void ProcessThread(ScuWorkRequest request, CancellationToken cancellationToken) + { + Task.Run(() => Process(request, cancellationToken), cancellationToken); } private async Task Process(ScuWorkRequest request, CancellationToken cancellationToken) { - ScuWorkResponse response = null; + ScuWorkResponse? response = null; try { response = request.RequestType switch @@ -99,13 +108,13 @@ private async Task Process(ScuWorkRequest request, CancellationToken cancellatio } finally { - request.Complete(response); + request.Complete(response!); } } private async Task HandleCEchoRequest(ScuWorkRequest request, CancellationToken cancellationToken) { - Guard.Against.Null(request); + Guard.Against.Null(request, nameof(request)); var scuResponse = new ScuWorkResponse(); var manualResetEvent = new ManualResetEventSlim(); @@ -192,11 +201,10 @@ public Task StartAsync(CancellationToken cancellationToken) { var task = Task.Run(async () => { - await BackgroundProcessingAsync(cancellationToken).ConfigureAwait(false); + await BackgroundProcessing(cancellationToken).ConfigureAwait(false); }, CancellationToken.None); Status = ServiceStatus.Running; - _logger.ServiceRunning(ServiceName); if (task.IsCompleted) return task; return Task.CompletedTask; diff --git a/src/InformaticsGateway/Services/Scu/ScuWorkRequest.cs b/src/InformaticsGateway/Services/Scu/ScuWorkRequest.cs old mode 100644 new mode 100755 index 43fdb79fb..74b15f887 --- a/src/InformaticsGateway/Services/Scu/ScuWorkRequest.cs +++ b/src/InformaticsGateway/Services/Scu/ScuWorkRequest.cs @@ -25,7 +25,7 @@ namespace Monai.Deploy.InformaticsGateway.Services.Scu public class ScuWorkRequest : IDisposable { private readonly AsyncManualResetEvent _awaiter; - private ScuWorkResponse _response; + private ScuWorkResponse? _response; private bool _disposedValue; public string CorrelationId { get; } @@ -33,18 +33,20 @@ public class ScuWorkRequest : IDisposable public string HostIp { get; } public int Port { get; } public string AeTitle { get; } + public CancellationToken CancellationToken { get; } - public ScuWorkRequest(string correlationId, RequestType requestType, string hostIp, int port, string aeTitle) + public ScuWorkRequest(string correlationId, RequestType requestType, string hostIp, int port, string aeTitle, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(correlationId); - Guard.Against.NullOrWhiteSpace(hostIp); - Guard.Against.NullOrWhiteSpace(aeTitle); + Guard.Against.NullOrWhiteSpace(correlationId, nameof(correlationId)); + Guard.Against.NullOrWhiteSpace(hostIp, nameof(hostIp)); + Guard.Against.NullOrWhiteSpace(aeTitle, nameof(aeTitle)); CorrelationId = correlationId; RequestType = requestType; HostIp = hostIp; Port = port; AeTitle = aeTitle; + CancellationToken = cancellationToken; _awaiter = new AsyncManualResetEvent(false); } @@ -65,7 +67,7 @@ public void Complete(ScuWorkResponse response) public async Task WaitAsync(CancellationToken cancellationToken = default) { await _awaiter.WaitAsync(cancellationToken).ConfigureAwait(false); - return _response; + return _response!; } protected virtual void Dispose(bool disposing) diff --git a/src/InformaticsGateway/Services/Storage/IObjectUploadQueue.cs b/src/InformaticsGateway/Services/Storage/IObjectUploadQueue.cs index 94efddc3e..09deb2dc2 100644 --- a/src/InformaticsGateway/Services/Storage/IObjectUploadQueue.cs +++ b/src/InformaticsGateway/Services/Storage/IObjectUploadQueue.cs @@ -16,6 +16,7 @@ */ using System.Threading; +using System.Threading.Tasks; using Monai.Deploy.InformaticsGateway.Api.Storage; namespace Monai.Deploy.InformaticsGateway.Services.Storage @@ -36,6 +37,6 @@ internal interface IObjectUploadQueue /// The default implementation blocks the call until a file is available from the queue. /// /// Propagates notification that operations should be canceled. - FileStorageMetadata Dequeue(CancellationToken cancellationToken); + Task Dequeue(CancellationToken cancellationToken); } } diff --git a/src/InformaticsGateway/Services/Storage/ObjectUploadQueue.cs b/src/InformaticsGateway/Services/Storage/ObjectUploadQueue.cs index 0b4ecefa2..27f728c95 100644 --- a/src/InformaticsGateway/Services/Storage/ObjectUploadQueue.cs +++ b/src/InformaticsGateway/Services/Storage/ObjectUploadQueue.cs @@ -16,9 +16,9 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; using Ardalis.GuardClauses; using Microsoft.Extensions.Logging; using Monai.Deploy.InformaticsGateway.Api.Storage; @@ -39,7 +39,7 @@ public ObjectUploadQueue(ILogger logger) public void Queue(FileStorageMetadata file) { - Guard.Against.Null(file); + Guard.Against.Null(file, nameof(file)); _workItems.Enqueue(file); var process = Process.GetCurrentProcess(); @@ -47,7 +47,7 @@ public void Queue(FileStorageMetadata file) _logger.InstanceAddedToUploadQueue(file.Id, _workItems.Count, process.WorkingSet64 / 1024.0); } - public FileStorageMetadata Dequeue(CancellationToken cancellationToken) + public async Task Dequeue(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { @@ -56,6 +56,10 @@ public FileStorageMetadata Dequeue(CancellationToken cancellationToken) _logger.InstanceInUploadQueue(_workItems.Count); return reuslt; } + if (_workItems.IsEmpty) + { + await Task.Delay(100, cancellationToken).ConfigureAwait(false); + } } throw new OperationCanceledException("Cancellation requested."); } diff --git a/src/InformaticsGateway/Services/Storage/ObjectUploadService.cs b/src/InformaticsGateway/Services/Storage/ObjectUploadService.cs old mode 100644 new mode 100755 index 3e1cce3ee..fbbaffd7b --- a/src/InformaticsGateway/Services/Storage/ObjectUploadService.cs +++ b/src/InformaticsGateway/Services/Storage/ObjectUploadService.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * Copyright 2019-2021 NVIDIA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using System.Threading.Tasks.Dataflow; using Ardalis.GuardClauses; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -32,7 +31,6 @@ using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Logging; -using Monai.Deploy.InformaticsGateway.Repositories; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.Storage.API; using Polly; @@ -44,7 +42,6 @@ internal class ObjectUploadService : IHostedService, IMonaiService, IDisposable private readonly ILogger _logger; private readonly IObjectUploadQueue _uplaodQueue; private readonly IStorageService _storageService; - private readonly IServiceScopeFactory _serviceScopeFactory; private readonly CancellationTokenSource _cancellationTokenSource; private readonly IOptions _configuration; private readonly IServiceScope _scope; @@ -58,32 +55,32 @@ public ObjectUploadService( ILogger logger, IOptions configuration) { - _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + var localServiceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); _cancellationTokenSource = new CancellationTokenSource(); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - _scope = _serviceScopeFactory.CreateScope(); + _scope = localServiceScopeFactory.CreateScope(); _uplaodQueue = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IObjectUploadQueue)); _storageService = _scope.ServiceProvider.GetService() ?? throw new ServiceNotFoundException(nameof(IStorageService)); - } - private void BackgroundProcessing(CancellationToken cancellationToken) + private void BackgroundProcessingAsync(CancellationToken cancellationToken) { _logger.ServiceRunning(ServiceName); var tasks = new List(); try { + _logger.InitializeThreads(_configuration.Value.Storage.ConcurrentUploads); for (var i = 0; i < _configuration.Value.Storage.ConcurrentUploads; i++) { tasks.Add(Task.Run(async () => { - await StartWorker(i, cancellationToken); - })); + await StartWorker(i, _cancellationTokenSource.Token).ConfigureAwait(false); + }, cancellationToken)); } - Task.WaitAll(tasks.ToArray()); + Task.WaitAll(tasks.ToArray(), _cancellationTokenSource.Token); } catch (ObjectDisposedException ex) { @@ -106,8 +103,8 @@ private async Task StartWorker(int thread, CancellationToken cancellationToken) { try { - var item = _uplaodQueue.Dequeue(cancellationToken); - await ProcessObject(item); + var item = await _uplaodQueue.Dequeue(cancellationToken).ConfigureAwait(false); + await ProcessObject(thread, item).ConfigureAwait(false); } catch (OperationCanceledException ex) { @@ -119,13 +116,15 @@ private async Task StartWorker(int thread, CancellationToken cancellationToken) _logger.ErrorUploading(ex); } } + Status = ServiceStatus.Stopped; + _logger.ServiceStopping(ServiceName); } public Task StartAsync(CancellationToken cancellationToken) { var task = Task.Run(() => { - BackgroundProcessing(cancellationToken); + BackgroundProcessingAsync(cancellationToken); }, CancellationToken.None); Status = ServiceStatus.Running; @@ -143,11 +142,11 @@ public Task StopAsync(CancellationToken cancellationToken) return Task.CompletedTask; } - private async Task ProcessObject(FileStorageMetadata blob) + private async Task ProcessObject(int thread, FileStorageMetadata blob) { - Guard.Against.Null(blob); + Guard.Against.Null(blob, nameof(blob)); - using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "File ID", blob.Id }, { "CorrelationId", blob.CorrelationId } }); + using var loggerScope = _logger.BeginScope(new LoggingDataDictionary { { "Thread", thread }, { "File ID", blob.Id }, { "CorrelationId", blob.CorrelationId } }); var stopwatch = new Stopwatch(); try { @@ -158,16 +157,17 @@ private async Task ProcessObject(FileStorageMetadata blob) case DicomFileStorageMetadata dicom: if (!string.IsNullOrWhiteSpace(dicom.JsonFile.TemporaryPath)) { - await UploadFileAndConfirm(dicom.Id, dicom.JsonFile, dicom.Source, dicom.Workflows, _cancellationTokenSource.Token).ConfigureAwait(false); + await UploadFileAndConfirm(dicom.Id, dicom.JsonFile, dicom.DataOrigin.Source, dicom.Workflows, blob.PayloadId!, _cancellationTokenSource.Token).ConfigureAwait(false); } break; } - await UploadFileAndConfirm(blob.Id, blob.File, blob.Source, blob.Workflows, _cancellationTokenSource.Token).ConfigureAwait(false); + await UploadFileAndConfirm(blob.Id, blob.File, blob.DataOrigin.Source, blob.Workflows, blob.PayloadId!, _cancellationTokenSource.Token).ConfigureAwait(false); } catch (Exception ex) { - _logger.FailedToUploadFile(blob.Id, ex); + blob.SetFailed(); + _logger.FailedToUploadFile(blob.Id, blob.File.UploadPath, ex); } finally { @@ -176,51 +176,55 @@ private async Task ProcessObject(FileStorageMetadata blob) } } - private async Task UploadFileAndConfirm(string identifier, StorageObjectMetadata storageObjectMetadata, string source, List workflows, CancellationToken cancellationToken) + private async Task UploadFileAndConfirm(string identifier, StorageObjectMetadata storageObjectMetadata, string source, List workflows, string payloadId, CancellationToken cancellationToken) { - Guard.Against.NullOrWhiteSpace(identifier); - Guard.Against.Null(storageObjectMetadata); - Guard.Against.NullOrWhiteSpace(source); - Guard.Against.Null(workflows); + Guard.Against.NullOrWhiteSpace(identifier, nameof(identifier)); + Guard.Against.Null(storageObjectMetadata, nameof(storageObjectMetadata)); + Guard.Against.NullOrWhiteSpace(source, nameof(source)); + Guard.Against.Null(workflows, nameof(workflows)); if (storageObjectMetadata.IsUploaded) { return; } + await UploadFile(storageObjectMetadata, source, workflows, payloadId, cancellationToken).ConfigureAwait(false); var count = 3; - do + while ( + count-- > 0 && + !await VerifyExists(storageObjectMetadata.GetPayloadPath(Guid.Parse(payloadId)), cancellationToken).ConfigureAwait(false) + ) { /* no op */}; + + if (count <= 0) { - await UploadFile(storageObjectMetadata, source, workflows, cancellationToken).ConfigureAwait(false); - if (count-- <= 0) - { - throw new FileUploadException($"Failed to upload file after retries {identifier}."); - } - } while (!(await VerifyExists(storageObjectMetadata.GetTempStoragPath(_configuration.Value.Storage.RemoteTemporaryStoragePath)).ConfigureAwait(false))); + throw new FileUploadException($"Failed to upload file after retries {identifier}."); + } } - private async Task VerifyExists(string path) + private async Task VerifyExists(string path, CancellationToken cancellationToken) { - try - { - var result = await _storageService.VerifyObjectExistsAsync( - _configuration.Value.Storage.TemporaryStorageBucket, - new KeyValuePair(path, path)).ConfigureAwait(false); - - var exists = result.Key.Equals(path, StringComparison.OrdinalIgnoreCase); - _logger.VerifyFileExists(path, exists); - return exists; - } - catch (Exception ex) - { - _logger.FailedToVerifyFileExistence(path, ex); - throw; - } + return await Policy + .Handle() + .WaitAndRetryAsync( + _configuration.Value.Storage.Retries.RetryDelays, + (exception, timeSpan, retryCount, context) => + { + _logger.ErrorUploadingFileToTemporaryStore(timeSpan, retryCount, exception); + }) + .ExecuteAsync(async () => + { + using var internalCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + internalCancellationTokenSource.CancelAfter(_configuration.Value.Storage.StorageServiceListTimeout); + var exists = await _storageService.VerifyObjectExistsAsync(_configuration.Value.Storage.StorageServiceBucketName, path).ConfigureAwait(false); + _logger.VerifyFileExists(path, exists); + return exists; + }) + .ConfigureAwait(false); } - private async Task UploadFile(StorageObjectMetadata storageObjectMetadata, string source, List workflows, CancellationToken cancellationToken) + private async Task UploadFile(StorageObjectMetadata storageObjectMetadata, string source, List workflows, string payloadId, CancellationToken cancellationToken) { - _logger.UploadingFileToTemporaryStore(storageObjectMetadata.TemporaryPath); + _logger.UploadingFileToStoreage(storageObjectMetadata.TemporaryPath); var metadata = new Dictionary { { FileMetadataKeys.Source, source }, @@ -237,19 +241,25 @@ await Policy }) .ExecuteAsync(async () => { + if (storageObjectMetadata.IsUploaded) { return; } + + var bucket = _configuration.Value.Storage.StorageServiceBucketName; + var path = storageObjectMetadata.GetPayloadPath(Guid.Parse(payloadId)); + storageObjectMetadata.Data.Seek(0, System.IO.SeekOrigin.Begin); await _storageService.PutObjectAsync( - _configuration.Value.Storage.TemporaryStorageBucket, - storageObjectMetadata.GetTempStoragPath(_configuration.Value.Storage.RemoteTemporaryStoragePath), + bucket, + path, storageObjectMetadata.Data, storageObjectMetadata.Data.Length, storageObjectMetadata.ContentType, metadata, cancellationToken).ConfigureAwait(false); - storageObjectMetadata.SetUploaded(_configuration.Value.Storage.TemporaryStorageBucket); + storageObjectMetadata.SetUploaded(_configuration.Value.Storage.TemporaryStorageBucket); // deletes local file and sets uploaded to true + _logger.UploadedFileToStoreage(path); + storageObjectMetadata.SetMoved(_configuration.Value.Storage.StorageServiceBucketName); // set bucket, date moved, and move complete }) .ConfigureAwait(false); - _logger.UploadedFileToTemporaryStore(storageObjectMetadata.TemporaryPath); } protected virtual void Dispose(bool disposing) @@ -258,6 +268,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { + _cancellationTokenSource.Dispose(); _scope.Dispose(); } diff --git a/src/InformaticsGateway/Services/Storage/StoageInfoProvider.cs b/src/InformaticsGateway/Services/Storage/StoageInfoProvider.cs index 6e9eb0b17..8d8af6ed5 100644 --- a/src/InformaticsGateway/Services/Storage/StoageInfoProvider.cs +++ b/src/InformaticsGateway/Services/Storage/StoageInfoProvider.cs @@ -54,7 +54,7 @@ public long AvailableFreeSpace { get { - var driveInfo = _fileSystem.DriveInfo.FromDriveName(_localTemporaryStoragePath); + var driveInfo = _fileSystem.DriveInfo.New(_localTemporaryStoragePath); return driveInfo.AvailableFreeSpace; } } @@ -79,7 +79,7 @@ public StorageInfoProvider( private void Initialize() { - var driveInfo = _fileSystem.DriveInfo.FromDriveName(_localTemporaryStoragePath); + var driveInfo = _fileSystem.DriveInfo.New(_localTemporaryStoragePath); _reservedSpace = (long)(driveInfo.TotalSize * (1 - (_storageConfiguration.Watermark / 100.0))); _reservedSpace = Math.Max(_reservedSpace, _storageConfiguration.ReserveSpaceGB * OneGB); _logger.StorageInfoProviderStartup(_localTemporaryStoragePath, driveInfo.TotalSize, _reservedSpace); @@ -87,7 +87,7 @@ private void Initialize() private bool IsSpaceAvailable() { - var driveInfo = _fileSystem.DriveInfo.FromDriveName(_localTemporaryStoragePath); + var driveInfo = _fileSystem.DriveInfo.New(_localTemporaryStoragePath); var freeSpace = driveInfo.AvailableFreeSpace; diff --git a/src/InformaticsGateway/Services/UriService/IUriService.cs b/src/InformaticsGateway/Services/UriService/IUriService.cs new file mode 100644 index 000000000..6fc38e20e --- /dev/null +++ b/src/InformaticsGateway/Services/UriService/IUriService.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; + +namespace Monai.Deploy.InformaticsGateway.Services.UriService +{ + /// + /// Uri Service. + /// + public interface IUriService + { + /// + /// Gets Relative Uri path with filters as a string. + /// + /// Filters. + /// Route. + /// Relative Uri string. + public string GetPageUriString(PaginationFilter filter, string route); + } +} diff --git a/src/InformaticsGateway/Services/UriService/UriService.cs b/src/InformaticsGateway/Services/UriService/UriService.cs new file mode 100644 index 000000000..28f78488c --- /dev/null +++ b/src/InformaticsGateway/Services/UriService/UriService.cs @@ -0,0 +1,60 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using Microsoft.AspNetCore.WebUtilities; +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; + +namespace Monai.Deploy.InformaticsGateway.Services.UriService +{ + /// + /// Uri Service. + /// + public class UriService : IUriService + { + private readonly Uri _baseUri; + + /// + /// Initializes a new instance of the class. + /// + /// Base Url. + public UriService(Uri baseUri) + { + _baseUri = baseUri; + } + + /// + /// Gets page uri. + /// + /// Filters. + /// Route. + /// Uri. + public string GetPageUriString(PaginationFilter filter, string route) + { + if (_baseUri.ToString().EndsWith('/') && route.StartsWith('/')) + { + route = route.TrimStart('/'); + } + + var endpointUri = new Uri(string.Concat(_baseUri, route)); + var modifiedUri = QueryHelpers.AddQueryString(endpointUri.ToString(), "pageNumber", filter.PageNumber.ToString()!); + modifiedUri = QueryHelpers.AddQueryString(modifiedUri, "pageSize", filter?.PageSize?.ToString() ?? string.Empty); + var uri = new Uri(modifiedUri); + return uri.IsAbsoluteUri ? uri.PathAndQuery : uri.OriginalString; + } + } +} diff --git a/src/InformaticsGateway/Test/Common/DicomFileStorageMetadataExtensionsTest.cs b/src/InformaticsGateway/Test/Common/DicomFileStorageMetadataExtensionsTest.cs index a572279f4..1e5c7bf36 100644 --- a/src/InformaticsGateway/Test/Common/DicomFileStorageMetadataExtensionsTest.cs +++ b/src/InformaticsGateway/Test/Common/DicomFileStorageMetadataExtensionsTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; +using MongoDB.Bson; using Xunit; namespace Monai.Deploy.InformaticsGateway.Test.Common @@ -39,17 +41,20 @@ public async Task GivenADicomFileStorageMetadata_WhenSetDataStreamsIsCalledWithI Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "calling", + "called"); var dicom = InstanceGenerator.GenerateDicomFile(); var json = dicom.ToJson(DicomJsonOptions.Complete, false); - await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Memory).ConfigureAwait(false); + await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Memory).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(metadata.File.Data); Assert.NotNull(metadata.JsonFile.Data); var ms = new MemoryStream(); - await dicom.SaveAsync(ms).ConfigureAwait(false); + await dicom.SaveAsync(ms).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(ms.ToArray(), (metadata.File.Data as MemoryStream).ToArray()); var jsonFromStream = Encoding.UTF8.GetString((metadata.JsonFile.Data as MemoryStream).ToArray()); @@ -70,20 +75,29 @@ public async Task GivenADicomFileStorageMetadata_WhenSetDataStreamsIsCalledWithD Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "calling", + "called"); var dicom = InstanceGenerator.GenerateDicomFile(); var json = dicom.ToJson(DicomJsonOptions.Complete, false); - await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Disk, fileSystem, "/temp").ConfigureAwait(false); + await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Disk, fileSystem, "/temp").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(metadata.File.Data); Assert.NotNull(metadata.JsonFile.Data); var ms = new MemoryStream(); - await dicom.SaveAsync(ms).ConfigureAwait(false); - Assert.Equal(ms.ToArray(), (metadata.File.Data as MemoryStream).ToArray()); + await dicom.SaveAsync(ms).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + using var temporaryDataAsMemoryStream = new MemoryStream(); + metadata.File.Data.CopyTo(temporaryDataAsMemoryStream); - var jsonFromStream = Encoding.UTF8.GetString((metadata.JsonFile.Data as MemoryStream).ToArray()); + Assert.Equal(ms.ToArray(), temporaryDataAsMemoryStream.ToArray()); + + using var temporaryJsonDataAsMemoryStream = new MemoryStream(); + metadata.JsonFile.Data.CopyTo(temporaryJsonDataAsMemoryStream); + + var jsonFromStream = Encoding.UTF8.GetString(temporaryJsonDataAsMemoryStream.ToArray()); Assert.Equal(json.Trim(), jsonFromStream.Trim()); var dicomFileFromJson = DicomJson.ConvertJsonToDicom(json); @@ -98,7 +112,10 @@ public void GivenADicomFileStorageMetadataWithInvalidDSValue_WhenSetDataStreamsI Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "calling", + "called"); var dicom = InstanceGenerator.GenerateDicomFile(); #pragma warning disable CS0618 // Type or member is obsolete @@ -109,7 +126,7 @@ public void GivenADicomFileStorageMetadataWithInvalidDSValue_WhenSetDataStreamsI Assert.Throws(() => dicom.ToJson(DicomJsonOptions.Complete, true)); } - [Fact] + [Fact(Skip = "Disabled due to bug in 5.1.2")] public async Task GivenADicomFileStorageMetadataWithInvalidDSValue_WhenSetDataStreamsIsCalled_ExpectDataStreamsAreSet() { var metadata = new DicomFileStorageMetadata( @@ -117,7 +134,10 @@ public async Task GivenADicomFileStorageMetadataWithInvalidDSValue_WhenSetDataSt Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), - Guid.NewGuid().ToString()); + Guid.NewGuid().ToString(), + DataService.DicomWeb, + "calling", + "called"); var dicom = InstanceGenerator.GenerateDicomFile(); #pragma warning disable CS0618 // Type or member is obsolete @@ -126,13 +146,13 @@ public async Task GivenADicomFileStorageMetadataWithInvalidDSValue_WhenSetDataSt dicom.Dataset.Add(DicomTag.PixelSpacing, "0.68300002813334234392234", "0.2354257587243524352345"); var json = dicom.ToJson(DicomJsonOptions.Complete, false); - await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Memory).ConfigureAwait(false); + await metadata.SetDataStreams(dicom, json, TemporaryDataStorageLocation.Memory).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.NotNull(metadata.File.Data); Assert.NotNull(metadata.JsonFile.Data); var ms = new MemoryStream(); - await dicom.SaveAsync(ms).ConfigureAwait(false); + await dicom.SaveAsync(ms).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.Equal(ms.ToArray(), (metadata.File.Data as MemoryStream).ToArray()); var jsonFromStream = Encoding.UTF8.GetString((metadata.JsonFile.Data as MemoryStream).ToArray()); diff --git a/src/InformaticsGateway/Test/DummyMessagingService.cs b/src/InformaticsGateway/Test/DummyMessagingService.cs index 92c246c5c..b4efe17d9 100644 --- a/src/InformaticsGateway/Test/DummyMessagingService.cs +++ b/src/InformaticsGateway/Test/DummyMessagingService.cs @@ -49,7 +49,9 @@ internal class DummyMessagingService : IMessageBrokerPublisherService, IMessageB { public string Name => "Dummy Messaging Service"; #pragma warning disable CS0067 + public event ConnectionErrorHandler OnConnectionError; + #pragma warning restore CS0067 public void Acknowledge(MessageBase message) => throw new NotImplementedException(); diff --git a/src/InformaticsGateway/Test/DummyStorageService.cs b/src/InformaticsGateway/Test/DummyStorageService.cs index cc386965e..152c5dea8 100644 --- a/src/InformaticsGateway/Test/DummyStorageService.cs +++ b/src/InformaticsGateway/Test/DummyStorageService.cs @@ -35,6 +35,7 @@ internal class DummyStorageRegistrar : ServiceRegistrationBase internal class DummyStorageHealthCheck : HealthCheckRegistrationBase { public override IHealthChecksBuilder ConfigureAdminHealthCheck(IHealthChecksBuilder builder, HealthStatus? failureStatus = null, IEnumerable tags = null, TimeSpan? timeout = null) => builder; + public override IHealthChecksBuilder ConfigureHealthCheck(IHealthChecksBuilder builder, HealthStatus? failureStatus = null, IEnumerable tags = null, TimeSpan? timeout = null) => builder; } @@ -72,8 +73,8 @@ internal class DummyStorageService : IStorageService public Task RemoveObjectWithCredentialsAsync(string bucketName, string objectName, Credentials credentials, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public Task> VerifyObjectExistsAsync(string bucketName, KeyValuePair objectPair) => throw new NotImplementedException(); + public Task VerifyObjectExistsAsync(string bucketName, string artifactName, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public Task> VerifyObjectsExistAsync(string bucketName, Dictionary objectDict) => throw new NotImplementedException(); + public Task> VerifyObjectsExistAsync(string bucketName, IReadOnlyList artifactList, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } } diff --git a/src/InformaticsGateway/Test/Logging/FileLoggingTextFormatterTest.cs b/src/InformaticsGateway/Test/Logging/FileLoggingTextFormatterTest.cs deleted file mode 100644 index 313fb0004..000000000 --- a/src/InformaticsGateway/Test/Logging/FileLoggingTextFormatterTest.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2021-2022 MONAI Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Globalization; -using System.Text; -using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Logging; -using Xunit; - -namespace Monai.Deploy.InformaticsGateway.Test.Logging -{ - public class FileLoggingTextFormatterTest - { - [Fact(DisplayName = "BuildEntryText")] - public void BuildEntryText() - { - var sb = new StringBuilder(); - var timestamp = DateTimeOffset.Now; - var cateogry = "Test"; - var eventId = new EventId(100); - var message = "This is a test"; - var exception = new Exception("Exception"); - var scopeProvider = new LoggerExternalScopeProvider(); - scopeProvider.Push("StateA"); - scopeProvider.Push("StateB"); - - var formatter = FileLoggingTextFormatter.Default; - formatter.BuildEntryText( - sb, cateogry, LogLevel.Information, eventId, message, - exception, scopeProvider, timestamp); - - var result = sb.ToString(); - Assert.Contains(timestamp.ToLocalTime().ToString("o", CultureInfo.InvariantCulture), result); - Assert.Contains($"info: {cateogry}[{eventId.Id}] [StateA] [StateB] => {message}", result); - Assert.Contains("System.Exception: Exception", result); - } - } -} diff --git a/src/InformaticsGateway/Test/Monai.Deploy.InformaticsGateway.Test.csproj b/src/InformaticsGateway/Test/Monai.Deploy.InformaticsGateway.Test.csproj old mode 100644 new mode 100755 index c04041810..874675acd --- a/src/InformaticsGateway/Test/Monai.Deploy.InformaticsGateway.Test.csproj +++ b/src/InformaticsGateway/Test/Monai.Deploy.InformaticsGateway.Test.csproj @@ -1,5 +1,5 @@ - - - - net6.0 + net8.0 Monai.Deploy.InformaticsGateway.Test false Apache-2.0 true - + - - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - Always - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Plug-ins/Monai.Deploy.InformaticsGateway.Test.PlugIns.csproj b/src/InformaticsGateway/Test/Plug-ins/Monai.Deploy.InformaticsGateway.Test.PlugIns.csproj new file mode 100644 index 000000000..71716a013 --- /dev/null +++ b/src/InformaticsGateway/Test/Plug-ins/Monai.Deploy.InformaticsGateway.Test.PlugIns.csproj @@ -0,0 +1,27 @@ + + + + Monai.Deploy.InformaticsGateway.Test.PlugIns + Monai.Deploy.InformaticsGateway.Test.PlugIns + net8.0 + enable + enable + + + + + diff --git a/src/InformaticsGateway/Test/Plug-ins/TestInputDataPlugIns.cs b/src/InformaticsGateway/Test/Plug-ins/TestInputDataPlugIns.cs new file mode 100755 index 000000000..d26a9ccf1 --- /dev/null +++ b/src/InformaticsGateway/Test/Plug-ins/TestInputDataPlugIns.cs @@ -0,0 +1,85 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Test.PlugIns +{ + [PlugInName("TestInputDataPlugInAddWorkflow")] + public class TestInputDataPlugInAddWorkflow : IInputDataPlugIn + { + public static readonly string TestString = "TestInputDataPlugIn executed!"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + fileMetadata.Workflows.Add(TestString); + return Task.FromResult((dicomFile, fileMetadata)); + } + } + + [PlugInName("TestInputDataPlugInResumeWorkflow")] + public class TestInputDataPlugInResumeWorkflow : IInputDataPlugIn + { + public static readonly string WorkflowInstanceId = "ee04a4ac-abb3-412b-b3a7-662c96380379"; + public static readonly string TaskId = "45b20f97-2b38-4b9a-baeb-d15f9d496851"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + fileMetadata.WorkflowInstanceId = WorkflowInstanceId; + fileMetadata.TaskId = TaskId; + return Task.FromResult((dicomFile, fileMetadata)); + } + } + + [PlugInName("TestInputDataPlugInModifyDicomFile")] + public class TestInputDataPlugInModifyDicomFile : IInputDataPlugIn + { + public static readonly DicomTag ExpectedTag = DicomTag.PatientAddress; + public static readonly string ExpectedValue = "Added by TestInputDataPlugInModifyDicomFile"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + dicomFile.Dataset.Add(ExpectedTag, ExpectedValue); + fileMetadata.WorkflowInstanceId = "WorkflowInstanceId"; + fileMetadata.TaskId = "TaskId"; + return Task.FromResult((dicomFile, fileMetadata)); + } + } + + [PlugInName("TestInputDataPlugInVirtualAE")] + public class TestInputDataPlugInVirtualAE : IInputDataPlugIn + { + public static readonly DicomTag ExpectedTag = DicomTag.PatientAddress; + public static readonly string ExpectedValue = "Added by TestInputDataPlugInVirtualAE"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + dicomFile.Dataset.Add(ExpectedTag, ExpectedValue); + return Task.FromResult((dicomFile, fileMetadata)); + } + } +} diff --git a/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs b/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs new file mode 100755 index 000000000..c3b45c8f4 --- /dev/null +++ b/src/InformaticsGateway/Test/Plug-ins/TestInputHL7DataPlugs.cs @@ -0,0 +1,39 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System.Reflection; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; + +namespace Monai.Deploy.InformaticsGateway.Test.PlugIns +{ + [PlugInName("TestInputHL7DataPlugInAddWorkflow")] + public class TestInputHL7DataPlugInAddWorkflow : IInputHL7DataPlugIn + { + public static readonly string TestString = "HOSPITAL changed!"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(Message hl7Message, FileStorageMetadata fileMetadata)> ExecuteAsync(Message hl7File, FileStorageMetadata fileMetadata) + { + hl7File.SetValue("MSH.3", TestString); + hl7File = new Message(hl7File.SerializeMessage(false)); + hl7File.ParseMessage(); + fileMetadata.Workflows.Add(TestString); + return Task.FromResult((hl7File, fileMetadata)); + } + } +} diff --git a/src/InformaticsGateway/Test/Plug-ins/TestOutputDataPlugIns.cs b/src/InformaticsGateway/Test/Plug-ins/TestOutputDataPlugIns.cs new file mode 100755 index 000000000..8a95ca525 --- /dev/null +++ b/src/InformaticsGateway/Test/Plug-ins/TestOutputDataPlugIns.cs @@ -0,0 +1,52 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; + +namespace Monai.Deploy.InformaticsGateway.Test.PlugIns +{ + [PlugInName("TestOutputDataPlugInAddMessage")] + public class TestOutputDataPlugInAddMessage : IOutputDataPlugIn + { + public static readonly string ExpectedValue = "Hello from TestOutputDataPlugInAddMessage"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage)> ExecuteAsync(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage) + { + exportRequestDataMessage.Messages.Add(ExpectedValue); + return Task.FromResult((dicomFile, exportRequestDataMessage)); + } + } + + [PlugInName("TestOutputDataPlugInModifyDicomFile")] + public class TestOutputDataPlugInModifyDicomFile : IOutputDataPlugIn + { + public static readonly DicomTag ExpectedTag = DicomTag.PatientAddress; + public static readonly string ExpectedValue = "Added by TestOutputDataPlugInModifyDicomFile"; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public Task<(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage)> ExecuteAsync(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage) + { + dicomFile.Dataset.Add(ExpectedTag, ExpectedValue); + return Task.FromResult((dicomFile, exportRequestDataMessage)); + } + } +} diff --git a/src/InformaticsGateway/Test/ProgramTest.cs b/src/InformaticsGateway/Test/ProgramTest.cs index 8ea0ac349..30399fb4f 100644 --- a/src/InformaticsGateway/Test/ProgramTest.cs +++ b/src/InformaticsGateway/Test/ProgramTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,24 +15,17 @@ */ using System; -using System.IO; -using System.Reflection; +using xRetry; using Xunit; namespace Monai.Deploy.InformaticsGateway.Test { public class ProgramTest { - private const string PlugInDirectoryName = "plug-ins"; - - [Fact(DisplayName = "Program - runs properly")] + [RetryFact(maxRetries: 10, delayBetweenRetriesMs: 500, DisplayName = "Program - runs properly")] public void Startup_RunsProperly() { var workingDirectory = Environment.CurrentDirectory; - var plugInDirectory = Path.Combine(workingDirectory, PlugInDirectoryName); - Directory.CreateDirectory(plugInDirectory); - var file = Assembly.GetExecutingAssembly().Location; - File.Copy(file, Path.Combine(plugInDirectory, Path.GetFileName(file)), true); var host = Program.CreateHostBuilder(System.Array.Empty()).Build(); Assert.NotNull(host); diff --git a/src/InformaticsGateway/Test/Repositories/MonaiServiceLocatorTest.cs b/src/InformaticsGateway/Test/Repositories/MonaiServiceLocatorTest.cs old mode 100644 new mode 100755 index 012425de7..0202492e3 --- a/src/InformaticsGateway/Test/Repositories/MonaiServiceLocatorTest.cs +++ b/src/InformaticsGateway/Test/Repositories/MonaiServiceLocatorTest.cs @@ -15,6 +15,8 @@ */ using System; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Repositories; using Monai.Deploy.InformaticsGateway.Services.Common; @@ -37,23 +39,30 @@ public MonaiServiceLocatorTest() mock.SetupGet(p => p.ServiceName).Returns(type.Name); return mock.Object; }); + + _serviceProvider.Setup(sp => sp.GetService(typeof(IEnumerable))) + .Returns((Type type) => + { + var mock = new Mock(); + return new List { mock.Object }; + }); } [Fact(DisplayName = "GetMonaiServices")] public void GetMonaiServices() { + var hosted = new List() + { + new Mock().Object + }; + + + var serviceLocator = new MonaiServiceLocator(_serviceProvider.Object); var result = serviceLocator.GetMonaiServices(); Assert.Collection(result, - items => items.ServiceName.Equals("DataRetrievalService"), - items => items.ServiceName.Equals("ScpService"), - items => items.ServiceName.Equals("ScuService"), - items => items.ServiceName.Equals("SpaceReclaimerService"), - items => items.ServiceName.Equals("DicomWebExportService"), - items => items.ServiceName.Equals("ScuExportService"), - items => items.ServiceName.Equals("PayloadNotificationService"), - items => items.ServiceName.Equals("HL7 Service")); + items => items.ServiceName.Equals("IPayloadService")); } [Fact(DisplayName = "GetServiceStatus")] @@ -62,7 +71,7 @@ public void GetServiceStatus() var serviceLocator = new MonaiServiceLocator(_serviceProvider.Object); var result = serviceLocator.GetServiceStatus(); - Assert.Equal(8, result.Count); + Assert.Equal(1, result.Count); foreach (var svc in result.Keys) { Assert.Equal(ServiceStatus.Running, result[svc]); diff --git a/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineFactoryTest.cs b/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineFactoryTest.cs new file mode 100755 index 000000000..55e63b0a5 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineFactoryTest.cs @@ -0,0 +1,79 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Moq; +using Xunit; +using Xunit.Abstractions; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputDataPlugInEngineFactoryTest + { + private readonly Mock> _logger; + private readonly IFileSystem _fileSystem; + private readonly ITestOutputHelper _output; + + public InputDataPlugInEngineFactoryTest(ITestOutputHelper output) + { + _logger = new Mock>(); + _fileSystem = new FileSystem(); + _output = output; + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void RegisteredPlugIns_WhenCalled_ReturnsListOfPlugIns() + { + var factory = new InputDataPlugInEngineFactory(_fileSystem, _logger.Object); + var result = factory.RegisteredPlugIns().OrderBy(p => p.Value).ToArray(); + + _output.WriteLine($"result now = {JsonSerializer.Serialize(result)}"); + + Assert.Collection(result, + p => VerifyPlugIn(p, typeof(DicomReidentifier)), + p => VerifyPlugIn(p, typeof(TestInputDataPlugInAddWorkflow)), + p => VerifyPlugIn(p, typeof(TestInputDataPlugInModifyDicomFile)), + p => VerifyPlugIn(p, typeof(TestInputDataPlugInResumeWorkflow)), + p => VerifyPlugIn(p, typeof(TestInputDataPlugInVirtualAE))); + + _logger.VerifyLogging($"{typeof(IInputDataPlugIn).Name} data plug-in found {typeof(TestInputDataPlugInAddWorkflow).GetCustomAttribute()?.Name}: {typeof(TestInputDataPlugInAddWorkflow).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IInputDataPlugIn).Name} data plug-in found {typeof(TestInputDataPlugInResumeWorkflow).GetCustomAttribute()?.Name}: {typeof(TestInputDataPlugInResumeWorkflow).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IInputDataPlugIn).Name} data plug-in found {typeof(TestInputDataPlugInModifyDicomFile).GetCustomAttribute()?.Name}: {typeof(TestInputDataPlugInModifyDicomFile).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IInputDataPlugIn).Name} data plug-in found {typeof(TestInputDataPlugInVirtualAE).GetCustomAttribute()?.Name}: {typeof(TestInputDataPlugInVirtualAE).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IInputDataPlugIn).Name} data plug-in found {typeof(DicomReidentifier).GetCustomAttribute()?.Name}: {typeof(DicomReidentifier).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + } + + private void VerifyPlugIn(KeyValuePair values, Type type) + { + Assert.Equal(values.Key, type.GetCustomAttribute()?.Name); + Assert.Equal(values.Value, type.GetShortTypeAssemblyName()); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineTest.cs b/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineTest.cs new file mode 100644 index 000000000..2cedd16f7 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputDataPluginEngineTest.cs @@ -0,0 +1,156 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Monai.Deploy.Messaging.Events; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputDataPlugInEngineTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly Mock _serviceScope; + private readonly ServiceProvider _serviceProvider; + + public InputDataPlugInEngineTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _serviceScope = new Mock(); + + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenAnInputDataPlugInEngine_WhenInitialized_ExpectParametersToBeValidated() + { + Assert.Throws(() => new InputDataPlugInEngine(null, null)); + Assert.Throws(() => new InputDataPlugInEngine(_serviceProvider, null)); + + _ = new InputDataPlugInEngine(_serviceProvider, _logger.Object); + } + + [Fact] + public void GivenAnInputDataPlugInEngine_WhenConfigureIsCalledWithBogusAssemblies_ThrowsException() + { + var pluginEngine = new InputDataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { "SomeBogusAssemblye" }; + + var exceptions = Assert.Throws(() => pluginEngine.Configure(assemblies)); + + Assert.Single(exceptions.InnerExceptions); + Assert.True(exceptions.InnerException is PlugInLoadingException); + Assert.Contains("Error loading plug-in 'SomeBogusAssemblye'", exceptions.InnerException.Message); + } + + [Fact] + public void GivenAnInputDataPlugInEngine_WhenConfigureIsCalledWithAValidAssembly_ExpectNoExceptions() + { + var pluginEngine = new InputDataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputDataPlugInAddWorkflow).AssemblyQualifiedName }; + + pluginEngine.Configure(assemblies); + Assert.NotNull(pluginEngine); + } + + [Fact] + public async Task GivenAnInputDataPlugInEngine_WhenExecutePlugInsIsCalledWithoutConfigure_ThrowsException() + { + var pluginEngine = new InputDataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputDataPlugInAddWorkflow).AssemblyQualifiedName }; + + var dicomFile = GenerateDicomFile(); + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + dicomFile.Dataset.GetString(DicomTag.StudyInstanceUID), + dicomFile.Dataset.GetString(DicomTag.SeriesInstanceUID), + dicomFile.Dataset.GetString(DicomTag.SOPInstanceUID), + DataService.DicomWeb, + "calling", + "called"); + + await Assert.ThrowsAsync(async () => await pluginEngine.ExecutePlugInsAsync(dicomFile, dicomInfo)); + } + + [Fact] + public async Task GivenAnInputDataPlugInEngine_WhenExecutePlugInsIsCalled_ExpectDataIsProcessedByPlugInAsync() + { + var pluginEngine = new InputDataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() + { + typeof(TestInputDataPlugInAddWorkflow).AssemblyQualifiedName, + typeof(TestInputDataPlugInModifyDicomFile).AssemblyQualifiedName, + typeof(TestInputDataPlugInResumeWorkflow).AssemblyQualifiedName, + }; + + pluginEngine.Configure(assemblies); + + var dicomFile = GenerateDicomFile(); + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + dicomFile.Dataset.GetString(DicomTag.StudyInstanceUID), + dicomFile.Dataset.GetString(DicomTag.SeriesInstanceUID), + dicomFile.Dataset.GetString(DicomTag.SOPInstanceUID), + DataService.DicomWeb, + "calling", + "called"); + + var (resultDicomFile, resultDicomInfo) = await pluginEngine.ExecutePlugInsAsync(dicomFile, dicomInfo); + + Assert.Equal(resultDicomFile, dicomFile); + Assert.Equal(resultDicomInfo, dicomInfo); + Assert.True(dicomInfo.Workflows.Contains(TestInputDataPlugInAddWorkflow.TestString)); + Assert.Equal(TestInputDataPlugInModifyDicomFile.ExpectedValue, resultDicomFile.Dataset.GetString(TestInputDataPlugInModifyDicomFile.ExpectedTag)); + + Assert.Equal(resultDicomInfo.WorkflowInstanceId, TestInputDataPlugInResumeWorkflow.WorkflowInstanceId); + Assert.Equal(resultDicomInfo.TaskId, TestInputDataPlugInResumeWorkflow.TaskId); + } + + private static DicomFile GenerateDicomFile() + { + var dataset = new DicomDataset + { + { DicomTag.PatientID, "PID" }, + { DicomTag.StudyInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SeriesInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SOPInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SOPClassUID, DicomUID.SecondaryCaptureImageStorage.UID } + }; + return new DicomFile(dataset); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs new file mode 100755 index 000000000..47a56a735 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineFactoryTest.cs @@ -0,0 +1,69 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Moq; +using Xunit; +using Xunit.Abstractions; +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputHL7DataPlugInEngineFactoryTest + { + private readonly Mock> _logger; + private readonly FileSystem _fileSystem; + private readonly ITestOutputHelper _output; + + public InputHL7DataPlugInEngineFactoryTest(ITestOutputHelper output) + { + _logger = new Mock>(); + _fileSystem = new FileSystem(); + _output = output; + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void RegisteredPlugIns_WhenCalled_ReturnsListOfPlugIns() + { + var factory = new InputHL7DataPlugInEngineFactory(_fileSystem, _logger.Object); + var result = factory.RegisteredPlugIns().OrderBy(p => p.Value).ToArray(); + + _output.WriteLine($"result now = {JsonSerializer.Serialize(result)}"); + + Assert.Collection(result, + p => VerifyPlugIn(p, typeof(TestInputHL7DataPlugInAddWorkflow))); + + _logger.VerifyLogging($"{typeof(IInputHL7DataPlugIn).Name} data plug-in found {typeof(TestInputHL7DataPlugInAddWorkflow).GetCustomAttribute()?.Name}: {typeof(TestInputHL7DataPlugInAddWorkflow).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + } + + private void VerifyPlugIn(KeyValuePair values, Type type) + { + Assert.Equal(values.Key, type.GetCustomAttribute()?.Name); + Assert.Equal(values.Value, type.GetShortTypeAssemblyName()); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs new file mode 100755 index 000000000..917cf5cf0 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/InputHL7DataPlugInEngineTest.cs @@ -0,0 +1,164 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using HL7.Dotnetcore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Monai.Deploy.Messaging.Events; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class InputHL7DataPlugInEngineTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly Mock _serviceScope; + private readonly ServiceProvider _serviceProvider; + + private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r\n"; + + public InputHL7DataPlugInEngineTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _serviceScope = new Mock(); + + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenInitialized_ExpectParametersToBeValidated() + { + Assert.Throws(() => new InputHL7DataPlugInEngine(null, null)); + Assert.Throws(() => new InputHL7DataPlugInEngine(_serviceProvider, null)); + + _ = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + } + + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenConfigureIsCalledWithBogusAssemblies_ThrowsException() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { "SomeBogusAssemblye" }; + + var exceptions = Assert.Throws(() => pluginEngine.Configure(assemblies)); + + Assert.Single(exceptions.InnerExceptions); + Assert.True(exceptions.InnerException is PlugInLoadingException); + Assert.Contains("Error loading plug-in 'SomeBogusAssemblye'", exceptions.InnerException.Message); + } + + [Fact] + public void GivenAnInputHL7DataPlugInEngine_WhenConfigureIsCalledWithAValidAssembly_ExpectNoExceptions() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName }; + + pluginEngine.Configure(assemblies); + Assert.NotNull(pluginEngine); + } + + [Fact] + public async Task GivenAnInputHL7DataPlugInEngine_WhenExecutePlugInsIsCalledWithoutConfigure_ThrowsException() + { + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() { typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName }; + + var config = new Hl7ApplicationConfigEntity() + { + PlugInAssemblies = assemblies, + DataMapping = new List() + { + new StringKeyValuePair() { Key = "MSH.3", Value = "MSH.3" }, + }, + }; + + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + "StudyInstanceUID", + "SeriesInstanceUID", + "SOPInstanceUID", + DataService.DicomWeb, + "calling", + "called"); + + var message = new HL7.Dotnetcore.Message(SampleMessage); + message.ParseMessage(true); + + await Assert.ThrowsAsync(async () => await pluginEngine.ExecutePlugInsAsync(message, dicomInfo, config)); + } + + [Fact] + public async Task GivenAnInputHL7DataPlugInEngine_WhenExecutePlugInsIsCalled_ExpectDataIsProcessedByPlugInAsync() + { + + var pluginEngine = new InputHL7DataPlugInEngine(_serviceProvider, _logger.Object); + var assemblies = new List() + { + typeof(TestInputHL7DataPlugInAddWorkflow).AssemblyQualifiedName, + }; + + var config = new Hl7ApplicationConfigEntity() + { + PlugInAssemblies = assemblies, + DataMapping = new List() + { + new StringKeyValuePair() { Key = "MSH.3", Value = "MSH.3" }, + }, + }; + + pluginEngine.Configure(assemblies); + + var dicomInfo = new DicomFileStorageMetadata( + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + "StudyInstanceUID", + "SeriesInstanceUID", + "SOPInstanceUID", + DataService.DicomWeb, + "calling", + "called"); + + var message = new HL7.Dotnetcore.Message(SampleMessage); + message.ParseMessage(true); + + var (Hl7Message, resultDicomInfo) = await pluginEngine.ExecutePlugInsAsync(message, dicomInfo, config); + + Assert.NotEqual(Hl7Message, message); + Assert.Equal(resultDicomInfo, dicomInfo); + Assert.Equal(Hl7Message.GetValue("MSH.3"), TestInputHL7DataPlugInAddWorkflow.TestString); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineFactoryTest.cs b/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineFactoryTest.cs new file mode 100755 index 000000000..5b01fd4db --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineFactoryTest.cs @@ -0,0 +1,84 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO.Abstractions; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Moq; +using Xunit; +using Xunit.Abstractions; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class OutputDataPlugInEngineFactoryTest + { + private readonly Mock> _logger; + private readonly FileSystem _fileSystem; + private readonly ITestOutputHelper _output; + + public OutputDataPlugInEngineFactoryTest(ITestOutputHelper output) + { + _logger = new Mock>(); + _fileSystem = new FileSystem(); + _output = output; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + + } + + [Fact] + public void RegisteredPlugIns_WhenCalled_ReturnsListOfPlugIns() + { + var factory = new OutputDataPlugInEngineFactory(_fileSystem, _logger.Object); + + var result = factory.RegisteredPlugIns().OrderBy(r => r.Value).ToArray(); + + _output.WriteLine($"result now = {JsonSerializer.Serialize(result)}"); + + Assert.Equal(3, result.Length); + + Assert.Equal(typeof(DicomDeidentifier).GetCustomAttribute().Name, result[0].Key); + Assert.Equal(typeof(TestOutputDataPlugInAddMessage).GetCustomAttribute().Name, result[1].Key); + Assert.Equal(typeof(TestOutputDataPlugInModifyDicomFile).GetCustomAttribute().Name, result[2].Key); + + + Assert.Collection(result, + p => VerifyPlugIn(p, typeof(DicomDeidentifier)), + p => VerifyPlugIn(p, typeof(TestOutputDataPlugInAddMessage)), + p => VerifyPlugIn(p, typeof(TestOutputDataPlugInModifyDicomFile)) + ); + + _logger.VerifyLogging($"{typeof(IOutputDataPlugIn).Name} data plug-in found {typeof(TestOutputDataPlugInAddMessage).GetCustomAttribute()?.Name}: {typeof(TestOutputDataPlugInAddMessage).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IOutputDataPlugIn).Name} data plug-in found {typeof(TestOutputDataPlugInModifyDicomFile).GetCustomAttribute()?.Name}: {typeof(TestOutputDataPlugInModifyDicomFile).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"{typeof(IOutputDataPlugIn).Name} data plug-in found {typeof(DicomDeidentifier).GetCustomAttribute()?.Name}: {typeof(DicomDeidentifier).GetShortTypeAssemblyName()}.", LogLevel.Information, Times.Once()); + } + + private void VerifyPlugIn(KeyValuePair values, Type type) + { + Assert.Equal(values.Key, type.GetCustomAttribute()?.Name); + Assert.Equal(values.Value, type.GetShortTypeAssemblyName()); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineTest.cs b/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineTest.cs new file mode 100755 index 000000000..faee1ca97 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/OutputDataPluginEngineTest.cs @@ -0,0 +1,153 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common +{ + public class OutputDataPlugInEngineTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly Mock _serviceScope; + private readonly IDicomToolkit _dicomToolkit; + private readonly ServiceProvider _serviceProvider; + + public OutputDataPlugInEngineTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _serviceScope = new Mock(); + _dicomToolkit = new DicomToolkit(); + + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => _dicomToolkit); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenAnOutputDataPlugInEngine_WhenInitialized_ExpectParametersToBeValidated() + { + Assert.Throws(() => new OutputDataPlugInEngine(null, null, null)); + Assert.Throws(() => new OutputDataPlugInEngine(_serviceProvider, null, null)); + Assert.Throws(() => new OutputDataPlugInEngine(_serviceProvider, _logger.Object, null)); + + _ = new OutputDataPlugInEngine(_serviceProvider, _logger.Object, _dicomToolkit); + } + + [Fact] + public void GivenAnOutputDataPlugInEngine_WhenConfigureIsCalledWithBogusAssemblies_ThrowsException() + { + var pluginEngine = new OutputDataPlugInEngine(_serviceProvider, _logger.Object, _dicomToolkit); + var assemblies = new List() { "SomeBogusAssemblye" }; + + var exceptions = Assert.Throws(() => pluginEngine.Configure(assemblies)); + + Assert.Single(exceptions.InnerExceptions); + Assert.True(exceptions.InnerException is PlugInLoadingException); + Assert.Contains("Error loading plug-in 'SomeBogusAssemblye'", exceptions.InnerException.Message); + } + + [Fact] + public void GivenAnOutputDataPlugInEngine_WhenConfigureIsCalledWithAValidAssembly_ExpectNoExceptions() + { + var pluginEngine = new OutputDataPlugInEngine(_serviceProvider, _logger.Object, _dicomToolkit); + var assemblies = new List() { typeof(TestOutputDataPlugInAddMessage).AssemblyQualifiedName }; + + pluginEngine.Configure(assemblies); + Assert.NotNull(pluginEngine); + } + + [Fact] + public async Task GivenAnOutputDataPlugInEngine_WhenExecutePlugInsIsCalledWithoutConfigure_ThrowsException() + { + var pluginEngine = new OutputDataPlugInEngine(_serviceProvider, _logger.Object, _dicomToolkit); + var assemblies = new List() { typeof(TestOutputDataPlugInAddMessage).AssemblyQualifiedName }; + + var message = new ExportRequestDataMessage(new Messaging.Events.ExportRequestEvent + { + CorrelationId = Guid.NewGuid().ToString(), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + }, "filename.dcm"); + + await Assert.ThrowsAsync(async () => await pluginEngine.ExecutePlugInsAsync(message)); + } + + [Fact] + public async Task GivenAnOutputDataPlugInEngine_WhenExecutePlugInsIsCalled_ExpectDataIsProcessedByPlugInAsync() + { + var pluginEngine = new OutputDataPlugInEngine(_serviceProvider, _logger.Object, _dicomToolkit); + var assemblies = new List() + { + typeof(TestOutputDataPlugInAddMessage).AssemblyQualifiedName, + typeof(TestOutputDataPlugInModifyDicomFile).AssemblyQualifiedName + }; + + pluginEngine.Configure(assemblies); + + var dicomFile = GenerateDicomFile(); + var message = new ExportRequestDataMessage(new Messaging.Events.ExportRequestEvent + { + CorrelationId = Guid.NewGuid().ToString(), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + }, "filename.dcm"); + using var ms = new MemoryStream(); + await dicomFile.SaveAsync(ms); + message.SetData(ms.ToArray()); + + var resultMessage = await pluginEngine.ExecutePlugInsAsync(message); + using var resultMs = new MemoryStream(resultMessage.FileContent); + var resultDicomFile = await DicomFile.OpenAsync(resultMs); + + Assert.Equal(resultMessage, message); + Assert.True(resultMessage.Messages.Contains(TestOutputDataPlugInAddMessage.ExpectedValue)); + Assert.Equal(TestOutputDataPlugInModifyDicomFile.ExpectedValue, resultDicomFile.Dataset.GetString(TestOutputDataPlugInModifyDicomFile.ExpectedTag)); + } + + private static DicomFile GenerateDicomFile() + { + var dataset = new DicomDataset + { + { DicomTag.PatientID, "PID" }, + { DicomTag.StudyInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SeriesInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SOPInstanceUID, DicomUIDGenerator.GenerateDerivedFromUUID() }, + { DicomTag.SOPClassUID, DicomUID.SecondaryCaptureImageStorage.UID } + }; + return new DicomFile(dataset); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Common/Pagination/PagedResponseTest.cs b/src/InformaticsGateway/Test/Services/Common/Pagination/PagedResponseTest.cs new file mode 100644 index 000000000..31cbc41b9 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Common/Pagination/PagedResponseTest.cs @@ -0,0 +1,43 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; +using Monai.Deploy.InformaticsGateway.Services.UriService; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Common.Pagination +{ + public class PagedResponseTest + { + [Fact] + public void SetUp_GivenExpectedInput_ReturnsExpectedResult() + { + var filter = new PaginationFilter(); + var data = new List { "orange", "apple", "donkey" }; + var pagedResponse = new PagedResponse>(data, 0, 3); + var uriService = new UriService(new Uri("https://test.com")); + pagedResponse.SetUp(filter, 9, uriService, "test"); + + Assert.Equal(pagedResponse.FirstPage, "/test?pageNumber=1&pageSize=3"); + Assert.Equal(pagedResponse.LastPage, "/test?pageNumber=3&pageSize=3"); + Assert.Equal(pagedResponse.NextPage, "/test?pageNumber=2&pageSize=3"); + Assert.Null(pagedResponse.PreviousPage); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Connectors/DataRetrievalServiceTest.cs b/src/InformaticsGateway/Test/Services/Connectors/DataRetrievalServiceTest.cs old mode 100644 new mode 100755 index 1e6694b15..f7b445681 --- a/src/InformaticsGateway/Test/Services/Connectors/DataRetrievalServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Connectors/DataRetrievalServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; using Moq; using Moq.Protected; using xRetry; @@ -125,9 +126,9 @@ public async Task GivenARunningDataRetrievalService_WhenStopAsyncIsCalled_Expect var store = new DataRetrievalService(_logger.Object, _serviceScopeFactory.Object, _options); await store.StartAsync(cancellationTokenSource.Token); - Thread.Sleep(250); + await Task.Delay(250); await store.StopAsync(cancellationTokenSource.Token); - Thread.Sleep(500); + await Task.Delay(500); Assert.Equal(ServiceStatus.Stopped, store.Status); _logger.VerifyLogging($"Data Retrieval Service is running.", LogLevel.Information, Times.Once()); @@ -145,9 +146,9 @@ public async Task GivenARunningDataRetrievalService_WhenStorageSpaceIsLow_Expect var store = new DataRetrievalService(_logger.Object, _serviceScopeFactory.Object, _options); await store.StartAsync(cancellationTokenSource.Token); - Thread.Sleep(250); + await Task.Delay(250); await store.StopAsync(cancellationTokenSource.Token); - Thread.Sleep(500); + await Task.Delay(500); _logger.VerifyLogging($"Data Retrieval Service is running.", LogLevel.Information, Times.Once()); _logger.VerifyLogging($"Data Retrieval Service is stopping.", LogLevel.Information, Times.Once()); @@ -191,21 +192,21 @@ public async Task GivenAInferenceRequestWithFromTheDatabaseWithPendingDownloads_ var restoredFile = new List { - new DicomFileStorageMetadata(Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString()), - new FhirFileStorageMetadata(Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(), FhirStorageFormat.Json) + new DicomFileStorageMetadata(Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),Guid.NewGuid().ToString(),DataService.DicomWeb,"calling","called"), + new FhirFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), FhirStorageFormat.Json, DataService.FHIR, "origin"), }; _storageMetadataWrapperRepository.Setup(p => p.GetFileStorageMetdadataAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(restoredFile); + .ReturnsAsync(restoredFile); _inferenceRequestStore.SetupSequence(p => p.TakeAsync(It.IsAny())) - .Returns(Task.FromResult(request)) - .Returns(() => - { - cancellationTokenSource.Cancel(); - throw new OperationCanceledException("canceled"); - }); + .Returns(Task.FromResult(request)) + .Returns(() => + { + cancellationTokenSource.Cancel(); + throw new OperationCanceledException("canceled"); + }); - _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny())); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny())); var store = new DataRetrievalService(_logger.Object, _serviceScopeFactory.Object, _options); @@ -327,7 +328,7 @@ public async Task GivenAnInferenceRequest_WhenItCompletesRetrievalWithoutAnyFile req.RequestUri.ToString().StartsWith($"{url}studies/")), ItExpr.IsAny()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); _logger.VerifyLogging($"Error processing request: TransactionId = {request.TransactionId}.", LogLevel.Error, Times.Once()); } @@ -461,7 +462,7 @@ public async Task GivenAnInferenceRequestWithDicomUids_WhenProcessing_ExpectAllI ItExpr.IsAny()); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(4)); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Exactly(4)); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(4)); } [RetryFact(5, 250)] @@ -579,7 +580,7 @@ public async Task GivenAnInferenceRequestWithPatientId_WhenProcessing_ExpectAllI } _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(studyInstanceUids.Count)); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Exactly(studyInstanceUids.Count)); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(studyInstanceUids.Count)); } [RetryFact(5, 250)] @@ -697,7 +698,7 @@ public async Task GivenAnInferenceRequestWithAccessionNumber_WhenProcessing_Expe } _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(2)); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Exactly(2)); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); } [RetryFact(5, 250)] @@ -807,7 +808,7 @@ public async Task GivenAnInferenceRequestWithFhirResources_WhenProcessing_Expect req.RequestUri.PathAndQuery.Contains("Observation/2")), ItExpr.IsAny()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Exactly(2)); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(2)); } private static HttpResponseMessage GenerateQueryResult(DicomTag dicomTag, string queryValue, List studyInstanceUids) @@ -843,4 +844,4 @@ private static void BlockUntilCancelled(CancellationToken token) WaitHandle.WaitAll(new[] { token.WaitHandle }); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/Connectors/PayloadAssemblerTest.cs b/src/InformaticsGateway/Test/Services/Connectors/PayloadAssemblerTest.cs old mode 100644 new mode 100755 index 82a62c62d..8bd3f390f --- a/src/InformaticsGateway/Test/Services/Connectors/PayloadAssemblerTest.cs +++ b/src/InformaticsGateway/Test/Services/Connectors/PayloadAssemblerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; using Moq; using xRetry; using Xunit; @@ -33,7 +34,7 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Connectors { public class PayloadAssemblerTest { - private readonly IOptions _options; + private readonly IOptions _options; private readonly Mock> _logger; private readonly Mock _serviceScopeFactory; @@ -45,7 +46,7 @@ public PayloadAssemblerTest() { _serviceScopeFactory = new Mock(); _repository = new Mock(); - _options = Options.Create(new InformaticsGatewayConfiguration()); + _options = Options.Create(new DatabaseOptions()); _logger = new Mock>(); _cancellationTokenSource = new CancellationTokenSource(); @@ -61,25 +62,27 @@ public PayloadAssemblerTest() _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; } [Fact] public void GivenAPayloadAssembler_WhenInitialized_ExpectParametersToBeValidated() { - Assert.Throws(() => new PayloadAssembler(null, null, null)); - Assert.Throws(() => new PayloadAssembler(_options, null, null)); - Assert.Throws(() => new PayloadAssembler(_options, _logger.Object, null)); + Assert.Throws(() => new PayloadAssembler(null, null)); + Assert.Throws(() => new PayloadAssembler(_logger.Object, null)); } [RetryFact(10, 200)] public async Task GivenAFileStorageMetadata_WhenQueueingWihtoutSpecifyingATimeout_ExpectDefaultTimeoutToBeUsed() { - var payloadAssembler = new PayloadAssembler(_options, _logger.Object, _serviceScopeFactory.Object); + var payloadAssembler = new PayloadAssembler(_logger.Object, _serviceScopeFactory.Object); _ = Assert.ThrowsAsync(async () => await Task.Run(() => payloadAssembler.Dequeue(_cancellationTokenSource.Token))); - await payloadAssembler.Queue("A", new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt")); + await payloadAssembler.Queue( + "A", + new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }), + new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }); _logger.VerifyLogging($"Bucket A created with timeout {PayloadAssembler.DEFAULT_TIMEOUT}s.", LogLevel.Information, Times.Once()); payloadAssembler.Dispose(); @@ -91,7 +94,7 @@ public async Task GivenFileStorageMetadataInTheDatabase_AtServiceStartup_ExpectP { _repository.Setup(p => p.RemovePendingPayloadsAsync(It.IsAny())); - var payloadAssembler = new PayloadAssembler(_options, _logger.Object, _serviceScopeFactory.Object); + var payloadAssembler = new PayloadAssembler(_logger.Object, _serviceScopeFactory.Object); await Task.Delay(250); payloadAssembler.Dispose(); _cancellationTokenSource.Cancel(); @@ -102,11 +105,14 @@ public async Task GivenFileStorageMetadataInTheDatabase_AtServiceStartup_ExpectP [RetryFact(10, 200)] public async Task GivenAPayloadAssembler_WhenDisposed_ExpectResourceToBeCleanedUp() { - var payloadAssembler = new PayloadAssembler(_options, _logger.Object, _serviceScopeFactory.Object); + var payloadAssembler = new PayloadAssembler(_logger.Object, _serviceScopeFactory.Object); _ = Assert.ThrowsAsync(async () => await Task.Run(() => payloadAssembler.Dequeue(_cancellationTokenSource.Token))); - await payloadAssembler.Queue("A", new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt")); + await payloadAssembler.Queue( + "A", + new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }), + new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }); payloadAssembler.Dispose(); _cancellationTokenSource.Cancel(); @@ -116,28 +122,33 @@ public async Task GivenAPayloadAssembler_WhenDisposed_ExpectResourceToBeCleanedU } [RetryFact(10, 200)] - public async Task GivenAPayloadThatHasNotCompleteUploads_WhenProcessedByTimedEvent_ExpectToBeAddedToQueue() + public async Task GivenAPayloadThatHasIncompleteUploads_WhenProcessedByTimedEvent_ExpectToBeRemovedFromQueue() { - var payloadAssembler = new PayloadAssembler(_options, _logger.Object, _serviceScopeFactory.Object); + var payloadAssembler = new PayloadAssembler(_logger.Object, _serviceScopeFactory.Object); - var file = new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt"); - file.File.SetUploaded("bucket"); + var file1 = new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }); + var file2 = new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }); - await payloadAssembler.Queue("A", file, 1); - await Task.Delay(1001); + await payloadAssembler.Queue("A", file1, new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + await payloadAssembler.Queue("A", file2, new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); + + file1.SetFailed(); + file2.SetUploaded(); + await Task.Delay(1101); payloadAssembler.Dispose(); - _repository.Verify(p => p.UpdateAsync(It.Is(p => p.State == Payload.PayloadState.Move), It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(It.Is(p => p.State == Payload.PayloadState.Move), It.IsAny()), Times.Never()); + _logger.VerifyLoggingMessageBeginsWith($"Payload (A) with 2 files deleted due to 1 upload failure(s).", LogLevel.Error, Times.Once()); } [RetryFact(10, 200)] public async Task GivenAPayloadThatHasCompletedUploads_WhenProcessedByTimedEvent_ExpectToBeAddedToQueue() { - var payloadAssembler = new PayloadAssembler(_options, _logger.Object, _serviceScopeFactory.Object); + var payloadAssembler = new PayloadAssembler(_logger.Object, _serviceScopeFactory.Object); - var file = new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt"); + var file = new TestStorageInfo(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "file1", ".txt", new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }); file.File.SetUploaded("bucket"); - await payloadAssembler.Queue("A", file, 1); + await payloadAssembler.Queue("A", file, new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 1); await Task.Delay(1001); var result = payloadAssembler.Dequeue(_cancellationTokenSource.Token); payloadAssembler.Dispose(); diff --git a/src/InformaticsGateway/Test/Services/Connectors/PayloadMoveActionHandlerTest.cs b/src/InformaticsGateway/Test/Services/Connectors/PayloadMoveActionHandlerTest.cs index a0e2ea4b9..2f63e787d 100644 --- a/src/InformaticsGateway/Test/Services/Connectors/PayloadMoveActionHandlerTest.cs +++ b/src/InformaticsGateway/Test/Services/Connectors/PayloadMoveActionHandlerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Services.Connectors; +using Monai.Deploy.Messaging.Events; using Monai.Deploy.Storage.API; using Moq; using xRetry; @@ -69,8 +70,7 @@ public PayloadMoveActionHandlerTest() _options.Value.Storage.Retries.DelaysMilliseconds = new[] { 5, 5, 5 }; _options.Value.Storage.StorageServiceBucketName = "bucket"; - _storageService.Setup(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny>())) - .Returns((string _, KeyValuePair input) => Task.FromResult(input)); + _storageService.Setup(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); } [RetryFact(10, 200)] @@ -95,13 +95,13 @@ public async Task GivenAPayloadInIncorrectState_WhenHandlerIsCalled_ExpectExcept }); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { State = Payload.PayloadState.Created, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -128,14 +128,14 @@ public async Task GivenAPayload_WhenHandlerFailedToCopyFiles_ExpectToBePutBackIn .ThrowsAsync(new Exception("error")); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = retryCount, State = Payload.PayloadState.Move, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -160,14 +160,14 @@ public async Task GivenAPayloadThatHasReachedMaximumRetries_WhenHandlerFailedToC .ThrowsAsync(new Exception("error")); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = 3, State = Payload.PayloadState.Move, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -191,14 +191,19 @@ public async Task GivenAPayload_WhenAllFilesAreMove_ExpectPayloadToBeAddedToNoti }); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var file1 = new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"); + file1.File.SetMoved("test"); + file1.JsonFile.SetMoved("test"); + var file2 = new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json, Messaging.Events.DataService.FHIR, "origin"); + file2.File.SetMoved("test"); + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = 3, State = Payload.PayloadState.Move, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json), + file1, + file2, }, }; @@ -208,8 +213,8 @@ public async Task GivenAPayload_WhenAllFilesAreMove_ExpectPayloadToBeAddedToNoti Assert.True(notifyEvent.Wait(TimeSpan.FromSeconds(5))); - _storageService.Verify(p => p.CopyObjectAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2)); - _storageService.Verify(p => p.RemoveObjectAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2)); + //_storageService.Verify(p => p.CopyObjectAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2)); + //_storageService.Verify(p => p.RemoveObjectAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2)); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationActionHandlerTest.cs b/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationActionHandlerTest.cs old mode 100644 new mode 100755 index 0066a4fd1..788031117 --- a/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationActionHandlerTest.cs +++ b/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationActionHandlerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.Messaging.API; +using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; using Moq; using Xunit; @@ -66,6 +67,7 @@ public PayloadNotificationActionHandlerTest() _cancellationTokenSource = new CancellationTokenSource(); + _options.Value.Messaging.Retries.DelaysMilliseconds = new[] { 5, 5, 5 }; _options.Value.Storage.Retries.DelaysMilliseconds = new[] { 5, 5, 5 }; _options.Value.Storage.StorageServiceBucketName = "bucket"; } @@ -89,13 +91,13 @@ public async Task GivenAPayloadInIncorrectState_WhenHandlerIsCalled_ExpectExcept }); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { State = Payload.PayloadState.Created, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -120,14 +122,14 @@ public async Task GivenAPayload_WhenHandlerFailedToPublishNotification_ExpectToB .Throws(new Exception("error")); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = retryCount, State = Payload.PayloadState.Notify, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -150,14 +152,14 @@ public async Task GivenAPayloadThatHasReachedMaximumRetries_WhenHandlerFailedToP .Throws(new Exception("error")); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = 3, State = Payload.PayloadState.Notify, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), }, }; @@ -176,14 +178,14 @@ public async Task GivenAPayload_WhenMessageIsPublished_ExpectPayloadToBeDeleted( }); var correlationId = Guid.NewGuid(); - var payload = new Payload("key", correlationId.ToString(), 0) + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) { RetryCount = 3, State = Payload.PayloadState.Notify, Files = new List { - new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()), - new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json), + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json, Messaging.Events.DataService.FHIR, "origin"), }, }; @@ -194,5 +196,62 @@ public async Task GivenAPayload_WhenMessageIsPublished_ExpectPayloadToBeDeleted( _messageBrokerPublisherService.Verify(p => p.Publish(It.IsAny(), It.IsAny()), Times.AtLeastOnce()); _repository.Verify(p => p.RemoveAsync(payload, _cancellationTokenSource.Token), Times.AtLeastOnce()); } + + [Fact] + public async Task GivenAPayload_ExpectMessageIsPublished() + { + var notifyAction = new ActionBlock(payload => + { + }); + + var correlationId = Guid.NewGuid(); + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), null, new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) + { + RetryCount = 3, + State = Payload.PayloadState.Notify, + Files = new List + { + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json, Messaging.Events.DataService.FHIR, "origin"), + }, + }; + + var handler = new PayloadNotificationActionHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var defaultKeys = new MessageBrokerConfigurationKeys(); + + await handler.NotifyAsync(payload, notifyAction, _cancellationTokenSource.Token); + + _messageBrokerPublisherService.Verify(p => p.Publish(defaultKeys.WorkflowRequest, It.IsAny()), Times.AtLeastOnce()); + } + + [Fact] + public async Task GivenAPayload_WithExternalAppSet_ExpectMessageIsPublished() + { + var notifyAction = new ActionBlock(payload => + { + }); + + var correlationId = Guid.NewGuid(); + var payload = new Payload("key", correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 0) + { + RetryCount = 3, + State = Payload.PayloadState.Notify, + Files = new List + { + new DicomFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "calling", "called"), + new FhirFileStorageMetadata(correlationId.ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Api.Rest.FhirStorageFormat.Json, Messaging.Events.DataService.FHIR, "origin"), + }, + WorkflowInstanceId = "WorkflowInstanceId", + TaskId = "TaskId" + }; + + + var handler = new PayloadNotificationActionHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var defaultKeys = new MessageBrokerConfigurationKeys(); + + await handler.NotifyAsync(payload, notifyAction, _cancellationTokenSource.Token); + + _messageBrokerPublisherService.Verify(p => p.Publish(defaultKeys.ArtifactRecieved, It.IsAny()), Times.AtLeastOnce()); + } } } diff --git a/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationServiceTest.cs b/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationServiceTest.cs old mode 100644 new mode 100755 index 45f405bac..649bcfdb6 --- a/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Connectors/PayloadNotificationServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Dataflow; @@ -30,7 +29,6 @@ using Monai.Deploy.InformaticsGateway.SharedTest; using Monai.Deploy.Messaging.API; using Monai.Deploy.Messaging.Events; -using Monai.Deploy.Messaging.Messages; using Moq; using xRetry; using Xunit; @@ -42,7 +40,7 @@ public class PayloadNotificationServiceTest private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; private readonly IOptions _options; - + private readonly IOptions _dbOptions; private readonly Mock _payloadAssembler; private readonly Mock _messageBrokerPublisherService; private readonly Mock _payloadNotificationActionHandler; @@ -58,6 +56,7 @@ public PayloadNotificationServiceTest() _serviceScopeFactory = new Mock(); _logger = new Mock>(); _options = Options.Create(new InformaticsGatewayConfiguration()); + _dbOptions = Options.Create(new DatabaseOptions()); _payloadAssembler = new Mock(); _messageBrokerPublisherService = new Mock(); @@ -79,7 +78,7 @@ public PayloadNotificationServiceTest() _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _options.Value.Database.Retries.DelaysMilliseconds = new[] { 1 }; + _dbOptions.Value.Retries.DelaysMilliseconds = new[] { 1 }; _options.Value.Storage.Retries.DelaysMilliseconds = new[] { 1 }; _options.Value.Storage.StorageServiceBucketName = "bucket"; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); @@ -96,7 +95,7 @@ public void GivenAPayloadNotificationService_AtInitialization_ExpectParametersTo [RetryFact(10, 200)] public async Task GivenThePayloadNotificationService_WhenStopAsyncIsCalled_ExpectServiceToStopAnyProcessing() { - var payload = new Payload("test", Guid.NewGuid().ToString(), 100) { State = Payload.PayloadState.Move }; + var payload = new Payload("test", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 100) { State = Payload.PayloadState.Move }; _payloadAssembler.Setup(p => p.Dequeue(It.IsAny())) .Returns(() => { @@ -122,9 +121,9 @@ public void GivenPayloadsStoredInTheDatabase_WhenServiceStarts_ExpectThePayloads { var testData = new List { - new Payload("created-test", Guid.NewGuid().ToString(), 10){ State = Payload.PayloadState.Created}, - new Payload("upload-test", Guid.NewGuid().ToString(), 10){ State = Payload.PayloadState.Move}, - new Payload("notification-test", Guid.NewGuid().ToString(), 10) {State = Payload.PayloadState.Notify}, + new Payload("created-test", Guid.NewGuid().ToString(),Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 10){ State = Payload.PayloadState.Created}, + new Payload("upload-test", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" },10){ State = Payload.PayloadState.Move}, + new Payload("notification-test", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" },10) {State = Payload.PayloadState.Notify}, }; _payloadRepository.Setup(p => p.GetPayloadsInStateAsync(It.IsAny(), It.IsAny())) @@ -144,7 +143,7 @@ public void GivenPayloadsStoredInTheDatabase_WhenServiceStarts_ExpectThePayloads public void GivenAPayload_WhenDequedFromPayloadAssemblerAndFailedToBeProcessByTheMoveActionHandler() { var resetEvent = new ManualResetEventSlim(); - var payload = new Payload("test", Guid.NewGuid().ToString(), 100) { State = Payload.PayloadState.Move }; + var payload = new Payload("test", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 100) { State = Payload.PayloadState.Move }; _payloadAssembler.Setup(p => p.Dequeue(It.IsAny())) .Returns(payload); @@ -157,12 +156,14 @@ public void GivenAPayload_WhenDequedFromPayloadAssemblerAndFailedToBeProcessByTh _ = service.StartAsync(_cancellationTokenSource.Token); resetEvent.Wait(); + + Assert.NotNull(service); } [RetryFact(10, 200)] public void GivenAPayload_WhenDequedFromPayloadAssembler_ExpectThePayloadBeProcessedByTheMoveActionHandler() { - var payload = new Payload("test", Guid.NewGuid().ToString(), 100) { State = Payload.PayloadState.Move }; + var payload = new Payload("test", Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), new DataOrigin { DataService = Messaging.Events.DataService.DIMSE, Destination = "dest", Source = "source" }, 100) { State = Payload.PayloadState.Move }; _payloadAssembler.Setup(p => p.Dequeue(It.IsAny())).Returns(payload); _payloadRepository.Setup(p => p.GetPayloadsInStateAsync(It.IsAny(), It.IsAny())).ReturnsAsync(new List()); @@ -176,25 +177,5 @@ public void GivenAPayload_WhenDequedFromPayloadAssembler_ExpectThePayloadBeProce _logger.VerifyLogging($"Payload {payload.PayloadId} added to {service.ServiceName} for processing.", LogLevel.Information, Times.AtLeastOnce()); } - - private bool VerifyHelper(Payload payload, Message message) - { - var workflowRequestEvent = message.ConvertTo(); - if (workflowRequestEvent is null) return false; - if (workflowRequestEvent.Payload.Count != 1) return false; - if (workflowRequestEvent.PayloadId != payload.PayloadId) return false; - if (workflowRequestEvent.FileCount != payload.Files.Count) return false; - if (workflowRequestEvent.CorrelationId != payload.CorrelationId) return false; - if (workflowRequestEvent.Timestamp != payload.DateTimeCreated) return false; - if (workflowRequestEvent.CallingAeTitle != payload.Files.First().Source) return false; - if (workflowRequestEvent.CalledAeTitle != payload.Files.OfType().First().CalledAeTitle) return false; - - var workflowInPayload = payload.GetWorkflows(); - if (workflowRequestEvent.Workflows.Count() != workflowInPayload.Count) return false; - if (workflowRequestEvent.Workflows.Except(workflowInPayload).Any()) return false; - if (workflowInPayload.Except(workflowRequestEvent.Workflows).Any()) return false; - - return true; - } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/DicomWeb/StowServiceTest.cs b/src/InformaticsGateway/Test/Services/DicomWeb/StowServiceTest.cs index b414c87a2..5b512a001 100644 --- a/src/InformaticsGateway/Test/Services/DicomWeb/StowServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/DicomWeb/StowServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,10 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Services.DicomWeb; using Moq; using Xunit; @@ -47,8 +50,10 @@ public class StowServiceTest private readonly Mock> _loggerMultipartDicomInstanceReader; private readonly Mock> _loggerSingleDicomInstanceReader; private readonly Mock _serviceScope; + private readonly Mock _repository; private readonly Mock _streamsWriter; - private readonly MockFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; + private readonly IServiceProvider _serviceProvider; public StowServiceTest() { @@ -58,29 +63,23 @@ public StowServiceTest() _loggerMultipartDicomInstanceReader = new Mock>(); _loggerSingleDicomInstanceReader = new Mock>(); _serviceScope = new Mock(); + _repository = new Mock(); _streamsWriter = new Mock(); _fileSystem = new MockFileSystem(); - var serviceProvider = new Mock(); - serviceProvider - .Setup(x => x.GetService(typeof(ILogger))) - .Returns(_logger.Object); - serviceProvider - .Setup(x => x.GetService(typeof(ILogger))) - .Returns(_loggerMultipartDicomInstanceReader.Object); - serviceProvider - .Setup(x => x.GetService(typeof(ILogger))) - .Returns(_loggerSingleDicomInstanceReader.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStreamsWriter))) - .Returns(_streamsWriter.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IFileSystem))) - .Returns(_fileSystem); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => _loggerMultipartDicomInstanceReader.Object); + services.AddScoped(p => _loggerSingleDicomInstanceReader.Object); + services.AddScoped(p => _streamsWriter.Object); + services.AddScoped(p => _fileSystem); + services.AddScoped(p => _repository.Object); + + _serviceProvider = services.BuildServiceProvider(); _serviceFactory.Setup(p => p.CreateScope()) .Returns(_serviceScope.Object); - _serviceScope.SetupGet(p => p.ServiceProvider).Returns(serviceProvider.Object); + _serviceScope.SetupGet(p => p.ServiceProvider).Returns(_serviceProvider); } [Fact(DisplayName = "Constructor Test")] @@ -93,16 +92,35 @@ public void ConstructorTest() Assert.Null(exception); } + [Fact(DisplayName = "StoreAsync - Throws when virtual AE cannot be found")] + public async Task StoreAsync_ThrowsWhenVirtualAECannotBeFound() + { + var correlationId = Guid.NewGuid().ToString(); + var service = new StowService(_serviceFactory.Object, _configuration); + + var httpRequest = new Mock(); + + await Assert.ThrowsAsync(async () => + await service.StoreAsync(httpRequest.Object, "a.b.c.d", "aet", "workflow", correlationId, CancellationToken.None)); + } + [Fact(DisplayName = "StoreAsync - Throws with bad StudyInstanceUID")] public async Task StoreAsync_ThrowsWithInvalidStudyInstanceUid() { var correlationId = Guid.NewGuid().ToString(); var service = new StowService(_serviceFactory.Object, _configuration); + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + }; + + _repository.Setup(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vae); var httpRequest = new Mock(); await Assert.ThrowsAsync(async () => - await service.StoreAsync(httpRequest.Object, "a.b.c.d", "workflow", correlationId, CancellationToken.None)); + await service.StoreAsync(httpRequest.Object, "a.b.c.d", "aet", "workflow", correlationId, CancellationToken.None)); } [Fact(DisplayName = "StoreAsync - Throws with bad content type header")] @@ -111,12 +129,19 @@ public async Task StoreAsync_ThrowsWithInvalidContentTypeHeader() var correlationId = Guid.NewGuid().ToString(); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; var service = new StowService(_serviceFactory.Object, _configuration); + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + }; + + _repository.Setup(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vae); var httpRequest = new Mock(); httpRequest.SetupGet(p => p.Headers.ContentType).Returns(new StringValues("invalid-header")); await Assert.ThrowsAsync(async () => - await service.StoreAsync(httpRequest.Object, studyInstanceUid, "workflow", correlationId, CancellationToken.None)); + await service.StoreAsync(httpRequest.Object, studyInstanceUid, "aet", "workflow", correlationId, CancellationToken.None)); } [Fact(DisplayName = "StoreAsync - Throws with unsupported content type")] @@ -125,12 +150,19 @@ public async Task StoreAsync_ThrowsWithUnsupportedContentTypeHeader() var correlationId = Guid.NewGuid().ToString(); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; var service = new StowService(_serviceFactory.Object, _configuration); + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + }; + + _repository.Setup(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vae); var httpRequest = new Mock(); httpRequest.SetupGet(p => p.ContentType).Returns(ContentTypes.ApplicationDicomJson); await Assert.ThrowsAsync(async () => - await service.StoreAsync(httpRequest.Object, studyInstanceUid, "workflow", correlationId, CancellationToken.None)); + await service.StoreAsync(httpRequest.Object, studyInstanceUid, "aet", "workflow", correlationId, CancellationToken.None)); } [Fact(DisplayName = "StoreAsync - handles single DICOM instance")] @@ -139,26 +171,59 @@ public async Task StoreAsync_HandlesSingleDicomInstance() var correlationId = Guid.NewGuid().ToString(); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; var service = new StowService(_serviceFactory.Object, _configuration); + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + }; - _streamsWriter.Setup(p => p.Save(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _streamsWriter.Setup(p => p.Save(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _repository.Setup(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vae); var httpRequest = new Mock(); httpRequest.SetupGet(p => p.ContentType).Returns(ContentTypes.ApplicationDicom); httpRequest.SetupGet(p => p.HttpContext.Connection.RemoteIpAddress).Returns(IPAddress.Loopback); httpRequest.SetupGet(p => p.Body).Returns(Stream.Null); - var exception = await Record.ExceptionAsync(async () => await service.StoreAsync(httpRequest.Object, studyInstanceUid, "workflow", correlationId, CancellationToken.None)); + var exception = await Record.ExceptionAsync(async () => await service.StoreAsync(httpRequest.Object, studyInstanceUid, "aet", "workflow", correlationId, CancellationToken.None)); Assert.Null(exception); } [Fact(DisplayName = "StoreAsync - handles multiple DICOM instances")] public async Task StoreAsync_HandlesMultipleDicomInstances() + { + var correlationId = Guid.NewGuid().ToString(); + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var service = new StowService(_serviceFactory.Object, _configuration); + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + }; + + _streamsWriter.Setup(p => p.Save(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _repository.Setup(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny())).ReturnsAsync(vae); + + var httpRequest = new Mock(); + httpRequest.SetupGet(p => p.ContentType).Returns($"{ContentTypes.MultipartRelated}; boundary={Boundary}"); + httpRequest.SetupGet(p => p.HttpContext.Connection.RemoteIpAddress).Returns(IPAddress.Loopback); + + var body = await GenerateMultipartData(); + + httpRequest.SetupGet(p => p.Body).Returns(new MemoryStream(body)); + + var exception = await Record.ExceptionAsync(async () => await service.StoreAsync(httpRequest.Object, studyInstanceUid, "aet", "workflow", correlationId, CancellationToken.None)); + Assert.Null(exception); + } + + [Fact(DisplayName = "StoreAsync - handles multiple DICOM instances without virtual AE")] + public async Task GivenMultipleDicomInstances_WhenStoreAsyncIsCalledWithoutVirtualAE_SavesStreams() { var correlationId = Guid.NewGuid().ToString(); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; var service = new StowService(_serviceFactory.Object, _configuration); - _streamsWriter.Setup(p => p.Save(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _streamsWriter.Setup(p => p.Save(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); var httpRequest = new Mock(); httpRequest.SetupGet(p => p.ContentType).Returns($"{ContentTypes.MultipartRelated}; boundary={Boundary}"); @@ -168,8 +233,10 @@ public async Task StoreAsync_HandlesMultipleDicomInstances() httpRequest.SetupGet(p => p.Body).Returns(new MemoryStream(body)); - var exception = await Record.ExceptionAsync(async () => await service.StoreAsync(httpRequest.Object, studyInstanceUid, "workflow", correlationId, CancellationToken.None)); + var exception = await Record.ExceptionAsync(async () => await service.StoreAsync(httpRequest.Object, studyInstanceUid, null, "workflow", correlationId, CancellationToken.None)); Assert.Null(exception); + + _repository.Verify(p => p.FindByAeTitleAsync(It.IsAny(), It.IsAny()), Times.Never()); } private async Task GenerateMultipartData() diff --git a/src/InformaticsGateway/Test/Services/DicomWeb/StreamsWriterTest.cs b/src/InformaticsGateway/Test/Services/DicomWeb/StreamsWriterTest.cs index 7e9323d8a..73f4d1d3d 100644 --- a/src/InformaticsGateway/Test/Services/DicomWeb/StreamsWriterTest.cs +++ b/src/InformaticsGateway/Test/Services/DicomWeb/StreamsWriterTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,11 @@ using FellowOakDicom; using FellowOakDicom.Network; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; @@ -31,6 +34,7 @@ using Monai.Deploy.InformaticsGateway.Services.DicomWeb; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; using Moq; using Xunit; @@ -38,34 +42,53 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.DicomWeb { public class StreamsWriterTest { + private readonly Mock _serviceScopeFactory; private readonly Mock> _logger; private readonly MockFileSystem _fileSystem; private readonly Mock _uploadQueue; private readonly Mock _dicomToolkit; private readonly Mock _payloadAssembler; + private readonly Mock _serviceScope; + private readonly Mock _inputDataPlugInEngine; private readonly IOptions _configuration; + private readonly IServiceProvider _serviceProvider; public StreamsWriterTest() { + _serviceScopeFactory = new Mock(); + _serviceScope = new Mock(); _uploadQueue = new Mock(); _dicomToolkit = new Mock(); _payloadAssembler = new Mock(); _configuration = Options.Create(new InformaticsGatewayConfiguration()); _logger = new Mock>(); + _inputDataPlugInEngine = new Mock(); _fileSystem = new MockFileSystem(); _configuration.Value.Storage.LocalTemporaryStoragePath = "./temp"; + + var services = new ServiceCollection(); + services.AddScoped(p => _inputDataPlugInEngine.Object); + + _serviceProvider = services.BuildServiceProvider(); + + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.SetupGet(p => p.ServiceProvider).Returns(_serviceProvider); + + _inputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny())) + .Returns((DicomFile dicomFile, FileStorageMetadata fileMetadata) => Task.FromResult(new Tuple(dicomFile, fileMetadata))); } [Fact] public void GivenAStreamsWriter_WhenInitialized_ExpectParametersToBeValidated() { - Assert.Throws(() => new StreamsWriter(null, null, null, null, null, null)); - Assert.Throws(() => new StreamsWriter(_uploadQueue.Object, null, null, null, null, null)); - Assert.Throws(() => new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, null, null, null, null)); - Assert.Throws(() => new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, null, null, null)); - Assert.Throws(() => new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, null, null)); - Assert.Throws(() => new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, null)); - var exception = Record.Exception(() => new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem)); + Assert.Throws(() => new StreamsWriter(null, null, null, null, null, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, null, null, null, null, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, null, null, null, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, null, null, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, null, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, null, null)); + Assert.Throws(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, null)); + var exception = Record.Exception(() => new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem)); Assert.Null(exception); } @@ -77,12 +100,13 @@ public async Task GivenAHttpStream_WhenFailedToOpenAsDicomInstance_ExpectStatus4 .Throws(new Exception("error")); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; - var writer = new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); var streams = GenerateDicomStreams(studyInstanceUid); var result = await writer.Save( streams, studyInstanceUid, + null, "Workflow", Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); @@ -96,16 +120,17 @@ public async Task GivingADicomInstanceWithoutAMatchingStudyInstanceUid_WhenSavei var uids = new List(); SetupDicomToolkitMocks(uids); - _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny())); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; - var writer = new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); var correlationId = Guid.NewGuid().ToString(); var streams = GenerateDicomStreams(studyInstanceUid); var result = await writer.Save( streams, DicomUIDGenerator.GenerateDerivedFromUUID().UID, + null, "Workflow", correlationId, Guid.NewGuid().ToString()); @@ -126,24 +151,158 @@ public async Task GivingADicomInstanceWithoutAMatchingStudyInstanceUid_WhenSavei } _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); - _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + } + + [Fact] + public async Task GivingAnEmptyRequest_WhenSaveingInstance_ExpectFailures() + { + var uids = new List(); + SetupDicomToolkitMocks(uids); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + + var correlationId = Guid.NewGuid().ToString(); + var streams = new List(); + var result = await writer.Save( + streams, + studyInstanceUid, + null, + "Workflow", + correlationId, + Guid.NewGuid().ToString()); + + Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); + + _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Never()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Fact] + public async Task GivingAValidDicomInstanceWithZeroLength_WhenSaveingInstance_ExpectFailures() + { + var uids = new List(); + SetupDicomToolkitMocks(uids); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + + var correlationId = Guid.NewGuid().ToString(); + var streams = new List() { Stream.Null, Stream.Null }; + var result = await writer.Save( + streams, + studyInstanceUid, + null, + "Workflow", + correlationId, + Guid.NewGuid().ToString()); + + Assert.Equal(StatusCodes.Status409Conflict, result.StatusCode); + + _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Never()); } [Fact] - public async Task GivingAValidDicomInstance_WhenSaveingInstance_ExpectInstanceToBeQueued() + public async Task GivingValidDicomInstances_WhenSaveingInstanceWithoutVirtualAE_ExpectInstanceToBeQueued() { var uids = new List(); SetupDicomToolkitMocks(uids); - _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny())); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + + var correlationId = Guid.NewGuid().ToString(); + var streams = GenerateDicomStreams(studyInstanceUid); + var result = await writer.Save( + streams, + studyInstanceUid, + null, + "Workflow", + correlationId, + Guid.NewGuid().ToString()); + + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + + var referencedSopSequence = result.Data.GetSequence(DicomTag.ReferencedSOPSequence); + Assert.Equal(streams.Count, referencedSopSequence.Items.Count); + + foreach (var item in referencedSopSequence.Items) + { + var uid = item.GetSingleValue(DicomTag.ReferencedSOPInstanceUID); + Assert.Contains(uids, p => p.SopInstanceUid == uid); + uid = item.GetSingleValue(DicomTag.ReferencedSOPClassUID); + Assert.Contains(uids, p => p.SopClassUid == uid); + } + + _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(streams.Count)); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(streams.Count)); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Exactly(streams.Count)); + } + + [Fact] + public async Task GivingValidDicomInstances_WhenUnableToOpenInstance_ExpectZeroFailedSOPSequence() + { + var uids = new List(); + + _dicomToolkit.Setup(p => p.OpenAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + + var correlationId = Guid.NewGuid().ToString(); + var streams = GenerateDicomStreams(studyInstanceUid); + var result = await writer.Save( + streams, + studyInstanceUid, + null, + "Workflow", + correlationId, + Guid.NewGuid().ToString()); + + Assert.Equal(StatusCodes.Status409Conflict, result.StatusCode); + + var failedSopSequence = result.Data.GetSequence(DicomTag.FailedSOPSequence); + Assert.Equal(0, failedSopSequence.Items.Count); + + _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Fact] + public async Task GivingValidDicomInstances_WhenSavingInstanceWithVirtualAE_ExpectInstanceToBeQueued() + { + var vae = new VirtualApplicationEntity + { + Name = Guid.NewGuid().ToString(), + VirtualAeTitle = Guid.NewGuid().ToString(), + PlugInAssemblies = new List { "A", "B" }, + }; + var uids = new List(); + SetupDicomToolkitMocks(uids); + _payloadAssembler.Setup(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; - var writer = new StreamsWriter(_uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); + var writer = new StreamsWriter(_serviceScopeFactory.Object, _uploadQueue.Object, _dicomToolkit.Object, _payloadAssembler.Object, _configuration, _logger.Object, _fileSystem); var correlationId = Guid.NewGuid().ToString(); var streams = GenerateDicomStreams(studyInstanceUid); var result = await writer.Save( streams, studyInstanceUid, + vae, "Workflow", correlationId, Guid.NewGuid().ToString()); @@ -162,7 +321,9 @@ public async Task GivingAValidDicomInstance_WhenSaveingInstance_ExpectInstanceTo } _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(streams.Count)); - _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny()), Times.Exactly(streams.Count)); + _payloadAssembler.Verify(p => p.Queue(It.Is(p => p == correlationId), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(streams.Count)); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Exactly(streams.Count)); } private void SetupDicomToolkitMocks(List uids) diff --git a/src/InformaticsGateway/Test/Services/Export/DicomWebExportServiceTest.cs b/src/InformaticsGateway/Test/Services/Export/DicomWebExportServiceTest.cs old mode 100644 new mode 100755 index 75708dde6..83e4c2036 --- a/src/InformaticsGateway/Test/Services/Export/DicomWebExportServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Export/DicomWebExportServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -27,6 +28,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; @@ -52,6 +55,7 @@ public class DicomWebExportServiceTest private readonly Mock _storageService; private readonly Mock _messageSubscriberService; private readonly Mock _messagePublisherService; + private readonly Mock _outputDataPlugInEngine; private readonly Mock _loggerFactory; private readonly Mock _httpClientFactory; private readonly Mock _inferenceRequestStore; @@ -69,6 +73,7 @@ public DicomWebExportServiceTest() _storageService = new Mock(); _messageSubscriberService = new Mock(); _messagePublisherService = new Mock(); + _outputDataPlugInEngine = new Mock(); _loggerFactory = new Mock(); _httpClientFactory = new Mock(); _inferenceRequestStore = new Mock(); @@ -81,28 +86,27 @@ public DicomWebExportServiceTest() _storageInfoProvider = new Mock(); _storageInfoProvider.Setup(p => p.HasSpaceAvailableForExport).Returns(true); - var serviceProvider = new Mock(); - serviceProvider - .Setup(x => x.GetService(typeof(IInferenceRequestRepository))) - .Returns(_inferenceRequestStore.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerPublisherService))) - .Returns(_messagePublisherService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerSubscriberService))) - .Returns(_messageSubscriberService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageService))) - .Returns(_storageService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageInfoProvider))) - .Returns(_storageInfoProvider.Object); + var services = new ServiceCollection(); + services.AddScoped(p => _inferenceRequestStore.Object); + services.AddScoped(p => _messagePublisherService.Object); + services.AddScoped(p => _messageSubscriberService.Object); + services.AddScoped(p => _outputDataPlugInEngine.Object); + services.AddScoped(p => _storageService.Object); + services.AddScoped(p => _storageInfoProvider.Object); + + var serviceProvider = services.BuildServiceProvider(); var scope = new Mock(); - scope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object); + scope.Setup(x => x.ServiceProvider).Returns(serviceProvider); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(scope.Object); + _outputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _outputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny())) + .Returns((ExportRequestDataMessage message) => Task.FromResult(message)); + + _configuration.Value.Export.Retries = new RetryConfiguration { DelaysMilliseconds = new[] { 5 } }; + _loggerFactory.Setup(p => p.CreateLogger(It.IsAny())).Returns(_loggerDicomWebClient.Object); _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } @@ -127,13 +131,13 @@ public async Task ExportDataBlockCallback_ReturnsNullIfInferenceRequestCannotBeF _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); + await messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -164,9 +168,9 @@ public async Task ExportDataBlockCallback_ReturnsNullIfInferenceRequestCannotBeF It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging($"The specified inference request '{transactionId}' cannot be found and will not be exported.", LogLevel.Error, Times.Once()); @@ -182,13 +186,13 @@ public async Task ExportDataBlockCallback_ReturnsNullIfInferenceRequestContainsN _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); + await messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -219,9 +223,9 @@ public async Task ExportDataBlockCallback_ReturnsNullIfInferenceRequestContainsN It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging($"The inference request contains no `outputResources` nor any DICOMweb export destinations.", LogLevel.Error, Times.Once()); @@ -248,13 +252,13 @@ public async Task ExportDataBlockCallback_RecordsStowFailuresAndReportFailure() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); + await messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -290,7 +294,7 @@ public async Task ExportDataBlockCallback_RecordsStowFailuresAndReportFailure() }; await service.StartAsync(_cancellationTokenSource.Token); - Assert.True(dataflowCompleted.WaitOne(3000)); + Assert.True(dataflowCompleted.WaitOne(5000)); await StopAndVerify(service); _messagePublisherService.Verify( @@ -298,9 +302,9 @@ public async Task ExportDataBlockCallback_RecordsStowFailuresAndReportFailure() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging($"Exporting data to {inferenceRequest.OutputResources.First().ConnectionDetails.Uri}.", LogLevel.Debug, Times.Once()); @@ -331,13 +335,13 @@ public async Task CompletesDataflow(HttpStatusCode httpStatusCode) _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); + await messageReceivedCallback(CreateMessageReceivedEventArgs(transactionId)); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -378,7 +382,7 @@ public async Task CompletesDataflow(HttpStatusCode httpStatusCode) }; await service.StartAsync(_cancellationTokenSource.Token); - Assert.True(dataflowCompleted.WaitOne(3000)); + Assert.True(dataflowCompleted.WaitOne(5000)); await StopAndVerify(service); _messagePublisherService.Verify( @@ -386,9 +390,9 @@ public async Task CompletesDataflow(HttpStatusCode httpStatusCode) It.Is(match => CheckMessage(match, (httpStatusCode == HttpStatusCode.OK ? ExportStatus.Success : ExportStatus.Failure), (httpStatusCode == HttpStatusCode.OK ? FileExportStatus.Success : FileExportStatus.ServiceError)))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging($"Exporting data to {inferenceRequest.OutputResources.First().ConnectionDetails.Uri}.", LogLevel.Debug, Times.AtLeastOnce()); @@ -413,7 +417,7 @@ public async Task CompletesDataflow(HttpStatusCode httpStatusCode) private bool CheckMessage(Message message, ExportStatus exportStatus, FileExportStatus fileExportStatus) { - Guard.Against.Null(message); + Guard.Against.Null(message, nameof(message)); var exportEvent = message.ConvertTo(); return exportEvent.Status == exportStatus && @@ -440,7 +444,7 @@ private async Task StopAndVerify(DicomWebExportService service) { await service.StopAsync(_cancellationTokenSource.Token); _logger.VerifyLogging($"{service.ServiceName} is stopping.", LogLevel.Information, Times.Once()); - Thread.Sleep(500); + await Task.Delay(500); } } } diff --git a/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs b/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs new file mode 100755 index 000000000..27677cce7 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Export/ExportHl7ServiceTests.cs @@ -0,0 +1,386 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; +using FellowOakDicom; +using FellowOakDicom.Network; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Mllp; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Export; +using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; +using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.API; +using Monai.Deploy.Messaging.Common; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Storage.API; +using Moq; +using xRetry; +using Xunit; + + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Export +{ + [Collection("Hl7 Export Listener")] + public class ExportHl7ServiceTests + { + private readonly Mock _storageService = new Mock(); + private readonly Mock _messageSubscriberService = new Mock(); + private readonly Mock _messagePublisherService = new Mock(); + private readonly Mock> _logger = new Mock>(); + private readonly Mock _extAppScpLogger = new Mock(); + private readonly Mock _serviceScopeFactory = new Mock(); + private readonly IOptions _configuration; + private readonly Mock _dicomToolkit = new Mock(); + private readonly Mock _storageInfoProvider = new Mock(); + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly Mock _mllpService = new Mock(); + private readonly Mock _outputDataPlugInEngine = new Mock(); + private readonly Mock _repository = new Mock(); + private readonly int _port = 1104; + + public ExportHl7ServiceTests() + { + _configuration = Options.Create(new InformaticsGatewayConfiguration()); + + var services = new ServiceCollection(); + services.AddScoped(p => _messagePublisherService.Object); + services.AddScoped(p => _messageSubscriberService.Object); + services.AddScoped(p => _storageService.Object); + services.AddScoped(p => _storageInfoProvider.Object); + services.AddScoped(p => _mllpService.Object); + services.AddScoped(p => _outputDataPlugInEngine.Object); + services.AddScoped(p => _repository.Object); + + var serviceProvider = services.BuildServiceProvider(); + + var scope = new Mock(); + scope.Setup(x => x.ServiceProvider).Returns(serviceProvider); + + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(scope.Object); + _configuration.Value.Export.Retries.DelaysMilliseconds = new[] { 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + _storageInfoProvider.Setup(p => p.HasSpaceAvailableForExport).Returns(true); + _outputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _outputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny())) + .Returns((ExportRequestDataMessage message) => Task.FromResult(message)); + } + + [RetryFact(1, 250, DisplayName = "Constructor - throws on null params")] + public void Constructor_ThrowsOnNullParams() + { + Assert.Throws(() => new Hl7ExportService(null, null, null, null, null)); + Assert.Throws(() => new Hl7ExportService(_logger.Object, null, null, null, null)); + Assert.Throws(() => new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, null, null, null)); + Assert.Throws(() => new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, null, null)); + Assert.Throws(() => new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, null)); + } + + + [RetryFact(10, 250, DisplayName = "When no destination is defined")] + public async Task ShallFailWhenNoDestinationIsDefined() + { + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs(string.Empty)); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + var service = new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _mllpService.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(3000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + _logger.VerifyLogging("Export task does not have destination set.", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "When destination is not configured")] + public async Task ShallFailWhenDestinationIsNotConfigured() + { + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(default(HL7DestinationEntity)); + + var service = new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _mllpService.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(3000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLogging($"Specified destination 'pacs' does not exist.", LogLevel.Error, Times.Once()); + } + + [RetryFact(1, 250, DisplayName = "HL7 message rejected")] + public async Task No_Ack_Sent() + { + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new HL7DestinationEntity { HostIp = "192.168.0.0", Port = _port }; + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _mllpService.Setup(p => p.SendMllp(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new Hl7SendException("Send exception")); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var service = new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _mllpService.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + + await StopAndVerify(service); + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.Verify(x => x.Log( + LogLevel.Error, + 538, // this is the eventId of the log we're looking for + It.Is((v, t) => true), + It.IsAny(), + It.Is>((v, t) => true))); + } + + [RetryFact(1, 250, DisplayName = "Failed to load message content")] + public async Task Error_Loading_HL7_Content() + { + + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new HL7DestinationEntity { HostIp = "192.168.0.0", Port = _port }; + var service = new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _mllpService.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(null)); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.DownloadError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLogging("Error downloading payload.", LogLevel.Error, Times.AtLeastOnce()); + _logger.VerifyLoggingMessageBeginsWith("Error downloading payload. Waiting ", LogLevel.Error, Times.AtLeastOnce()); + } + + [RetryFact(2, 250, DisplayName = "success after Hl7 send")] + public async Task Success_After_Hl7Send() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new HL7DestinationEntity { HostIp = "192.168.0.0", Port = _port }; + var service = new Hl7ExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _mllpService.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + { + messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Success, FileExportStatus.Success))), Times.Once()); + } + + + + + private bool CheckMessage(Message message, ExportStatus exportStatus, FileExportStatus fileExportStatus) + { + Guard.Against.Null(message, nameof(message)); + + var exportEvent = message.ConvertTo(); + return exportEvent.Status == exportStatus && + exportEvent.FileStatuses.First().Value == fileExportStatus; + } + + private static MessageReceivedEventArgs CreateMessageReceivedEventArgs(string destination) + { + var exportRequestEvent = new ExportRequestEvent + { + ExportTaskId = Guid.NewGuid().ToString(), + CorrelationId = Guid.NewGuid().ToString(), + Destinations = new string[] { destination }, + Files = new[] { "file1" }, + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + }; + var jsonMessage = new JsonMessage(exportRequestEvent, MessageBrokerConfiguration.InformaticsGatewayApplicationId, exportRequestEvent.CorrelationId, exportRequestEvent.DeliveryTag); + + return new MessageReceivedEventArgs(jsonMessage.ToMessage(), CancellationToken.None); + } + + private async Task StopAndVerify(Hl7ExportService service) + { + await service.StopAsync(_cancellationTokenSource.Token); + _logger.VerifyLogging($"{service.ServiceName} is stopping.", LogLevel.Information, Times.Once()); + await Task.Delay(500); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Export/ExportServiceBaseTest.cs b/src/InformaticsGateway/Test/Services/Export/ExportServiceBaseTest.cs old mode 100644 new mode 100755 index a014b496b..afdb3e6f0 --- a/src/InformaticsGateway/Test/Services/Export/ExportServiceBaseTest.cs +++ b/src/InformaticsGateway/Test/Services/Export/ExportServiceBaseTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,18 @@ */ using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Export; using Monai.Deploy.InformaticsGateway.Services.Storage; @@ -44,15 +49,16 @@ public class TestExportService : ExportServiceBase public event EventHandler ExportDataBlockCalled; public bool ExportShallFail = false; - protected override int Concurrency => 1; + protected override ushort Concurrency => 1; public override string RoutingKey => AgentName; public override string ServiceName { get => "Test Export Service"; } public TestExportService( ILogger logger, IOptions InformaticsGatewayConfiguration, - IServiceScopeFactory serviceScopeFactory) - : base(logger, InformaticsGatewayConfiguration, serviceScopeFactory) + IServiceScopeFactory serviceScopeFactory, + IDicomToolkit _dicomToolkit) + : base(logger, InformaticsGatewayConfiguration, serviceScopeFactory, _dicomToolkit) { } @@ -67,6 +73,38 @@ protected override Task ExportDataBlockCallback(Export return Task.FromResult(exportRequestData); } + + protected override async Task ProcessMessage(MessageReceivedEventArgs eventArgs) + { + var (exportFlow, reportingActionBlock) = SetupActionBlocks(); + + lock (SyncRoot) + { + var exportRequest = eventArgs.Message.ConvertTo(); + if (ExportRequests.ContainsKey(exportRequest.ExportTaskId)) + { + return; + } + + exportRequest.MessageId = eventArgs.Message.MessageId; + exportRequest.DeliveryTag = eventArgs.Message.DeliveryTag; + + var exportRequestWithDetails = new ExportRequestEventDetails(exportRequest); + + ExportRequests.Add(exportRequest.ExportTaskId, exportRequestWithDetails); + if (!exportFlow.Post(exportRequestWithDetails)) + { + MessageSubscriber.Reject(eventArgs.Message); + } + else + { + } + } + + exportFlow.Complete(); + await reportingActionBlock.Completion.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + } } public class ExportServiceBaseTest @@ -74,43 +112,44 @@ public class ExportServiceBaseTest private readonly Mock _storageService; private readonly Mock _messageSubscriberService; private readonly Mock _messagePublisherService; + private readonly Mock _outputDataPlugInEngine; private readonly Mock _logger; private readonly Mock _storageInfoProvider; private readonly IOptions _configuration; private readonly CancellationTokenSource _cancellationTokenSource; private readonly Mock _serviceScopeFactory; + private readonly Mock _dicomToolkit = new Mock(); public ExportServiceBaseTest() { _storageService = new Mock(); _messageSubscriberService = new Mock(); _messagePublisherService = new Mock(); + _outputDataPlugInEngine = new Mock(); _logger = new Mock(); _storageInfoProvider = new Mock(); _configuration = Options.Create(new InformaticsGatewayConfiguration()); _cancellationTokenSource = new CancellationTokenSource(); _serviceScopeFactory = new Mock(); - var serviceProvider = new Mock(); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerPublisherService))) - .Returns(_messagePublisherService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerSubscriberService))) - .Returns(_messageSubscriberService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageService))) - .Returns(_storageService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageInfoProvider))) - .Returns(_storageInfoProvider.Object); + var services = new ServiceCollection(); + services.AddScoped(p => _messagePublisherService.Object); + services.AddScoped(p => _messageSubscriberService.Object); + services.AddScoped(p => _outputDataPlugInEngine.Object); + services.AddScoped(p => _storageService.Object); + services.AddScoped(p => _storageInfoProvider.Object); + var serviceProvider = services.BuildServiceProvider(); var scope = new Mock(); - scope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object); + scope.Setup(x => x.ServiceProvider).Returns(serviceProvider); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(scope.Object); + _outputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _outputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny())) + .Returns((ExportRequestDataMessage message) => Task.FromResult(message)); + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); _storageInfoProvider.Setup(p => p.HasSpaceAvailableForExport).Returns(true); } @@ -118,7 +157,7 @@ public ExportServiceBaseTest() [RetryFact(5, 250, DisplayName = "Data flow test - can start/stop")] public async Task DataflowTest_StartStop() { - var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object); + var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object, _dicomToolkit.Object); await service.StartAsync(_cancellationTokenSource.Token); await StopAndVerify(service); @@ -133,23 +172,23 @@ public async Task DataflowTest_RejectOnInsufficientStorageSpace() _messageSubscriberService.Setup(p => p.Reject(It.IsAny(), It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback((string topic, string queue, Action messageReceivedCallback, ushort prefetchCount) => + .Callback((string topic, string queue, Func messageReceivedCallback, ushort prefetchCount) => { messageReceivedCallback(CreateMessageReceivedEventArgs()); }); - var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object); + var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object, _dicomToolkit.Object); await service.StartAsync(_cancellationTokenSource.Token); await StopAndVerify(service); _messageSubscriberService.Verify(p => p.Reject(It.IsAny(), It.IsAny()), Times.Once()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); } @@ -162,20 +201,20 @@ public async Task DataflowTest_PayloadDownlaodFailure() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs()); + await messageReceivedCallback(CreateMessageReceivedEventArgs()); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception("storage error")); var countdownEvent = new CountdownEvent(1); - var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object); + var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object, _dicomToolkit.Object); service.ReportActionCompleted += (sender, e) => { countdownEvent.Signal(); @@ -189,13 +228,12 @@ public async Task DataflowTest_PayloadDownlaodFailure() It.Is(match => (match.ConvertTo()).Status == ExportStatus.Failure)), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); } - [RetryFact(1, 10, DisplayName = "Data flow test - end to end workflow with partial failure")] public async Task DataflowTest_EndToEnd_WithPartialFailure() { @@ -206,15 +244,15 @@ public async Task DataflowTest_EndToEnd_WithPartialFailure() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { while (messageCount-- > 0) { - messageReceivedCallback(CreateMessageReceivedEventArgs()); + await messageReceivedCallback(CreateMessageReceivedEventArgs()); } }); @@ -222,7 +260,7 @@ public async Task DataflowTest_EndToEnd_WithPartialFailure() .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes(testData))); var countdownEvent = new CountdownEvent(5 * 3); - var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object); + var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object, _dicomToolkit.Object); service.ReportActionCompleted += (sender, e) => { countdownEvent.Signal(); @@ -236,7 +274,7 @@ public async Task DataflowTest_EndToEnd_WithPartialFailure() countdownEvent.Signal(); }; await service.StartAsync(_cancellationTokenSource.Token); - Assert.True(countdownEvent.Wait(1000000)); + Assert.True(countdownEvent.Wait(60000)); await StopAndVerify(service); _messagePublisherService.Verify( @@ -244,9 +282,9 @@ public async Task DataflowTest_EndToEnd_WithPartialFailure() It.Is(match => (match.ConvertTo()).Status == ExportStatus.PartialFailure)), Times.Exactly(5)); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Exactly(5)); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); } @@ -260,15 +298,15 @@ public async Task DataflowTest_EndToEnd() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { while (messageCount-- > 0) { - messageReceivedCallback(CreateMessageReceivedEventArgs()); + await messageReceivedCallback(CreateMessageReceivedEventArgs()); } }); @@ -276,7 +314,7 @@ public async Task DataflowTest_EndToEnd() .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes(testData))); var countdownEvent = new CountdownEvent(5 * 3); - var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object); + var service = new TestExportService(_logger.Object, _configuration, _serviceScopeFactory.Object, _dicomToolkit.Object); service.ReportActionCompleted += (sender, e) => { countdownEvent.Signal(); @@ -288,7 +326,7 @@ public async Task DataflowTest_EndToEnd() countdownEvent.Signal(); }; await service.StartAsync(_cancellationTokenSource.Token); - Assert.True(countdownEvent.Wait(1000000)); + Assert.True(countdownEvent.Wait(60000)); await StopAndVerify(service); _messagePublisherService.Verify( @@ -296,10 +334,12 @@ public async Task DataflowTest_EndToEnd() It.Is(match => (match.ConvertTo()).Status == ExportStatus.Success)), Times.Exactly(5)); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Exactly(5)); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); + _outputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Exactly(5 * 2)); + _outputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny()), Times.Exactly(5 * 2)); } internal static MessageReceivedEventArgs CreateMessageReceivedEventArgs() @@ -323,7 +363,7 @@ private async Task StopAndVerify(TestExportService service) { await service.StopAsync(_cancellationTokenSource.Token); _logger.VerifyLogging($"{service.ServiceName} is stopping.", LogLevel.Information, Times.Once()); - Thread.Sleep(250); + await Task.Delay(250); } } } diff --git a/src/InformaticsGateway/Test/Services/Export/ExtAppScuServiceTest.cs b/src/InformaticsGateway/Test/Services/Export/ExtAppScuServiceTest.cs new file mode 100755 index 000000000..689699ba6 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Export/ExtAppScuServiceTest.cs @@ -0,0 +1,595 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Ardalis.GuardClauses; +using FellowOakDicom; +using FellowOakDicom.Network; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Export; +using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.API; +using Monai.Deploy.Messaging.Common; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Storage.API; +using Moq; +using xRetry; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Export +{ + [Collection("SCP Listener")] + public class ExtAppScuServiceTest + { + private readonly Mock _storageService; + private readonly Mock _messageSubscriberService; + private readonly Mock _messagePublisherService; + private readonly Mock _outputDataPlugInEngine; + private readonly Mock> _logger; + private readonly Mock _extAppScpLogger; + private readonly Mock _serviceScopeFactory; + private readonly IOptions _configuration; + private readonly Mock _dicomToolkit; + private readonly Mock _repository; + private readonly Mock _storageInfoProvider; + private readonly Mock _externalAppRepository = new Mock(); + + private readonly CancellationTokenSource _cancellationTokenSource; + private readonly DicomScpFixture _dicomScp; + private readonly int _port = 1104; + + public ExtAppScuServiceTest(DicomScpFixture dicomScp) + { + _dicomScp = dicomScp ?? throw new ArgumentNullException(nameof(dicomScp)); + + _storageService = new Mock(); + _messageSubscriberService = new Mock(); + _messagePublisherService = new Mock(); + _outputDataPlugInEngine = new Mock(); + _logger = new Mock>(); + _extAppScpLogger = new Mock(); + _serviceScopeFactory = new Mock(); + _configuration = Options.Create(new InformaticsGatewayConfiguration()); + _dicomToolkit = new Mock(); + _cancellationTokenSource = new CancellationTokenSource(); + _repository = new Mock(); + _storageInfoProvider = new Mock(); + + var services = new ServiceCollection(); + services.AddScoped(p => _repository.Object); + services.AddScoped(p => _messagePublisherService.Object); + services.AddScoped(p => _messageSubscriberService.Object); + services.AddScoped(p => _outputDataPlugInEngine.Object); + services.AddScoped(p => _storageService.Object); + services.AddScoped(p => _storageInfoProvider.Object); + + var serviceProvider = services.BuildServiceProvider(); + + var scope = new Mock(); + scope.Setup(x => x.ServiceProvider).Returns(serviceProvider); + + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(scope.Object); + DicomScpFixture.Logger = _extAppScpLogger.Object; + _dicomScp.Start(_port); + _configuration.Value.Export.Retries.DelaysMilliseconds = new[] { 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + _storageInfoProvider.Setup(p => p.HasSpaceAvailableForExport).Returns(true); + + _outputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _outputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny())) + .Returns((ExportRequestDataMessage message) => Task.FromResult(message)); + + _externalAppRepository.Setup(r => r.GetAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult>(null)); + + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var testDicom = InstanceGenerator.GenerateDicomFile(seriesInstanceUid: seriesInstanceUid); + _dicomToolkit.Setup(d => d.Load(It.IsAny())).Returns(testDicom); + } + + [RetryFact(5, 250, DisplayName = "Constructor - throws on null params")] + public void Constructor_ThrowsOnNullParams() + { + Assert.Throws(() => new ExtAppScuExportService(null, null, null, null, null)); + Assert.Throws(() => new ExtAppScuExportService(_logger.Object, null, null, null, null)); + Assert.Throws(() => new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, null, null, null)); + Assert.Throws(() => new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, null, null)); + Assert.Throws(() => new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, null)); + } + + [RetryFact(10, 250, DisplayName = "When no destination is defined")] + public async Task ShallFailWhenNoDestinationIsDefined() + { + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs(string.Empty)); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(3000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + _logger.VerifyLogging("Export task does not have destination set.", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "When destination is not configured")] + public async Task ShallFailWhenDestinationIsNotConfigured() + { + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(default(DestinationApplicationEntity)); + + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(3000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLogging($"Specified destination 'pacs' does not exist.", LogLevel.Error, Times.Once()); + } + + [RetryFact(1, 250, DisplayName = "Association rejected")] + public async Task AssociationRejected() + { + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = "ABC", Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + + await StopAndVerify(service); + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLogging($"Association rejected.", LogLevel.Warning, Times.AtLeastOnce()); + _logger.VerifyLoggingMessageBeginsWith($"Association rejected with reason", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "C-STORE simulate abort")] + public async Task SimulateAbort() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = "ABORT", Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.ResourceLimitation; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + + await StopAndVerify(service); + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLoggingMessageBeginsWith($"Association aborted with reason", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "C-STORE Failure")] + public async Task CStoreFailure() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = DicomScpFixture.s_aETITLE, Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.ResourceLimitation; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + _logger.VerifyLogging("Association accepted.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"Failed to export with error {DicomStatus.ResourceLimitation}.", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "Failed to load DICOM content")] + public async Task ErrorLoadingDicomContent() + { + + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = DicomScpFixture.s_aETITLE, Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => + { + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Throws(new Exception("error")); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.UnsupportedDataType))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + + _logger.VerifyLoggingMessageBeginsWith("Error reading DICOM file: error", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "Unreachable Server")] + public async Task UnreachableServer() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = DicomScpFixture.s_aETITLE, Name = DicomScpFixture.s_aETITLE, HostIp = "UNKNOWNHOST123456789", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + { + messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(8000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + _logger.VerifyLoggingMessageBeginsWith("Association aborted with error", LogLevel.Error, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "C-STORE success")] + public async Task ExportCompletes() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = DicomScpFixture.s_aETITLE, Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + { + messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _messagePublisherService.Verify( + p => p.Publish(It.IsAny(), + It.Is(match => CheckMessage(match, ExportStatus.Success, FileExportStatus.Success))), Times.Once()); + _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); + _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny()), Times.Once()); + _logger.VerifyLogging("Association accepted.", LogLevel.Information, Times.Once()); + _logger.VerifyLogging($"Instance sent successfully.", LogLevel.Information, Times.Once()); + } + + [RetryFact(10, 250, DisplayName = "success save ExternalAppDetails")] + public async Task SaveExternalAppDetails() + { + _extAppScpLogger.Invocations.Clear(); + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var destination = new DestinationApplicationEntity { AeTitle = DicomScpFixture.s_aETITLE, Name = DicomScpFixture.s_aETITLE, HostIp = "localhost", Port = _port }; + var service = new ExtAppScuExportService(_logger.Object, _serviceScopeFactory.Object, _configuration, _dicomToolkit.Object, _externalAppRepository.Object); + + _messagePublisherService.Setup(p => p.Publish(It.IsAny(), It.IsAny())); + _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); + _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); + _messageSubscriberService.Setup( + p => p.SubscribeAsync(It.IsAny(), + It.IsAny(), + It.IsAny>(), + It.IsAny())) + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + { + messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + }); + + _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new MemoryStream(Encoding.UTF8.GetBytes("test"))); + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).ReturnsAsync(destination); + _dicomToolkit.Setup(p => p.Load(It.IsAny())).Returns(InstanceGenerator.GenerateDicomFile(sopInstanceUid: sopInstanceUid)); + + var dataflowCompleted = new ManualResetEvent(false); + service.ReportActionCompleted += (sender, args) => + { + dataflowCompleted.Set(); + }; + + DicomScpFixture.DicomStatus = DicomStatus.Success; + await service.StartAsync(_cancellationTokenSource.Token); + Assert.True(dataflowCompleted.WaitOne(5000)); + await StopAndVerify(service); + + _externalAppRepository.Verify(r => r.AddAsync(It.IsAny(), It.IsAny()), Times.Once); + } + + private bool CheckMessage(Message message, ExportStatus exportStatus, FileExportStatus fileExportStatus) + { + Guard.Against.Null(message, nameof(message)); + + var exportEvent = message.ConvertTo(); + return exportEvent.Status == exportStatus && + exportEvent.FileStatuses.First().Value == fileExportStatus; + } + + private static MessageReceivedEventArgs CreateMessageReceivedEventArgs(string destination) + { + var exportRequestEvent = new ExternalAppRequestEvent + { + ExportTaskId = Guid.NewGuid().ToString(), + CorrelationId = Guid.NewGuid().ToString(), + Targets = new List { new DataOrigin { Destination = destination } }, + Files = new[] { "file1" }, + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + }; + var jsonMessage = new JsonMessage(exportRequestEvent, MessageBrokerConfiguration.InformaticsGatewayApplicationId, exportRequestEvent.CorrelationId, exportRequestEvent.DeliveryTag); + + return new MessageReceivedEventArgs(jsonMessage.ToMessage(), CancellationToken.None); + } + + private async Task StopAndVerify(ExtAppScuExportService service) + { + await service.StopAsync(_cancellationTokenSource.Token); + _logger.VerifyLogging($"{service.ServiceName} is stopping.", LogLevel.Information, Times.Once()); + await Task.Delay(500); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Export/ScuExportServiceTest.cs b/src/InformaticsGateway/Test/Services/Export/ScuExportServiceTest.cs old mode 100644 new mode 100755 index 79f8cb10b..a832401aa --- a/src/InformaticsGateway/Test/Services/Export/ScuExportServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Export/ScuExportServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -26,7 +27,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; @@ -50,6 +52,7 @@ public class ScuExportServiceTest private readonly Mock _storageService; private readonly Mock _messageSubscriberService; private readonly Mock _messagePublisherService; + private readonly Mock _outputDataPlugInEngine; private readonly Mock> _logger; private readonly Mock _scpLogger; private readonly Mock _serviceScopeFactory; @@ -69,6 +72,7 @@ public ScuExportServiceTest(DicomScpFixture dicomScp) _storageService = new Mock(); _messageSubscriberService = new Mock(); _messagePublisherService = new Mock(); + _outputDataPlugInEngine = new Mock(); _logger = new Mock>(); _scpLogger = new Mock(); _serviceScopeFactory = new Mock(); @@ -78,25 +82,18 @@ public ScuExportServiceTest(DicomScpFixture dicomScp) _repository = new Mock(); _storageInfoProvider = new Mock(); - var serviceProvider = new Mock(); - serviceProvider - .Setup(x => x.GetService(typeof(IDestinationApplicationEntityRepository))) - .Returns(_repository.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerPublisherService))) - .Returns(_messagePublisherService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IMessageBrokerSubscriberService))) - .Returns(_messageSubscriberService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageService))) - .Returns(_storageService.Object); - serviceProvider - .Setup(x => x.GetService(typeof(IStorageInfoProvider))) - .Returns(_storageInfoProvider.Object); + var services = new ServiceCollection(); + services.AddScoped(p => _repository.Object); + services.AddScoped(p => _messagePublisherService.Object); + services.AddScoped(p => _messageSubscriberService.Object); + services.AddScoped(p => _outputDataPlugInEngine.Object); + services.AddScoped(p => _storageService.Object); + services.AddScoped(p => _storageInfoProvider.Object); + + var serviceProvider = services.BuildServiceProvider(); var scope = new Mock(); - scope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object); + scope.Setup(x => x.ServiceProvider).Returns(serviceProvider); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(scope.Object); DicomScpFixture.Logger = _scpLogger.Object; @@ -104,6 +101,10 @@ public ScuExportServiceTest(DicomScpFixture dicomScp) _configuration.Value.Export.Retries.DelaysMilliseconds = new[] { 1 }; _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); _storageInfoProvider.Setup(p => p.HasSpaceAvailableForExport).Returns(true); + + _outputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _outputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny())) + .Returns((ExportRequestDataMessage message) => Task.FromResult(message)); } [RetryFact(5, 250, DisplayName = "Constructor - throws on null params")] @@ -122,13 +123,13 @@ public async Task ShallFailWhenNoDestinationIsDefined() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs(string.Empty)); + await messageReceivedCallback(CreateMessageReceivedEventArgs(string.Empty)); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -151,11 +152,11 @@ public async Task ShallFailWhenNoDestinationIsDefined() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); - _logger.VerifyLogging("SCU Export configuration error: Export task does not have destination set.", LogLevel.Error, Times.Once()); + _logger.VerifyLogging("Export task does not have destination set.", LogLevel.Error, Times.Once()); } [RetryFact(10, 250, DisplayName = "When destination is not configured")] @@ -165,13 +166,13 @@ public async Task ShallFailWhenDestinationIsNotConfigured() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -196,12 +197,12 @@ public async Task ShallFailWhenDestinationIsNotConfigured() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ConfigurationError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); - _logger.VerifyLogging($"SCU Export configuration error: Specified destination 'pacs' does not exist.", LogLevel.Error, Times.Once()); + _logger.VerifyLogging($"Specified destination 'pacs' does not exist.", LogLevel.Error, Times.Once()); } [RetryFact(1, 250, DisplayName = "Association rejected")] @@ -214,13 +215,13 @@ public async Task AssociationRejected() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -246,9 +247,9 @@ public async Task AssociationRejected() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging($"Association rejected.", LogLevel.Warning, Times.AtLeastOnce()); @@ -267,13 +268,13 @@ public async Task SimulateAbort() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -298,9 +299,9 @@ public async Task SimulateAbort() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLoggingMessageBeginsWith($"Association aborted with reason", LogLevel.Error, Times.Once()); @@ -318,13 +319,13 @@ public async Task CStoreFailure() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -349,9 +350,9 @@ public async Task CStoreFailure() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging("Association accepted.", LogLevel.Information, Times.Once()); _logger.VerifyLogging($"Failed to export with error {DicomStatus.ResourceLimitation}.", LogLevel.Error, Times.Once()); @@ -369,13 +370,13 @@ public async Task ErrorLoadingDicomContent() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>(async (topic, queue, messageReceivedCallback, prefetchCount) => { - messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); + await messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); _storageService.Setup(p => p.GetObjectAsync(It.IsAny(), It.IsAny(), It.IsAny())) @@ -400,9 +401,9 @@ public async Task ErrorLoadingDicomContent() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.UnsupportedDataType))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLoggingMessageBeginsWith("Error reading DICOM file: error", LogLevel.Error, Times.Once()); @@ -420,11 +421,11 @@ public async Task UnreachableServer() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => { messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); @@ -451,9 +452,9 @@ public async Task UnreachableServer() It.Is(match => CheckMessage(match, ExportStatus.Failure, FileExportStatus.ServiceError))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLoggingMessageBeginsWith("Association aborted with error", LogLevel.Error, Times.Once()); } @@ -470,11 +471,11 @@ public async Task ExportCompletes() _messageSubscriberService.Setup(p => p.Acknowledge(It.IsAny())); _messageSubscriberService.Setup(p => p.RequeueWithDelay(It.IsAny())); _messageSubscriberService.Setup( - p => p.Subscribe(It.IsAny(), + p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny())) - .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => + .Callback, ushort>((topic, queue, messageReceivedCallback, prefetchCount) => { messageReceivedCallback(CreateMessageReceivedEventArgs("pacs")); }); @@ -501,9 +502,9 @@ public async Task ExportCompletes() It.Is(match => CheckMessage(match, ExportStatus.Success, FileExportStatus.Success))), Times.Once()); _messageSubscriberService.Verify(p => p.Acknowledge(It.IsAny()), Times.Once()); _messageSubscriberService.Verify(p => p.RequeueWithDelay(It.IsAny()), Times.Never()); - _messageSubscriberService.Verify(p => p.Subscribe(It.IsAny(), + _messageSubscriberService.Verify(p => p.SubscribeAsync(It.IsAny(), It.IsAny(), - It.IsAny>(), + It.IsAny>(), It.IsAny()), Times.Once()); _logger.VerifyLogging("Association accepted.", LogLevel.Information, Times.Once()); _logger.VerifyLogging($"Instance sent successfully.", LogLevel.Information, Times.Once()); @@ -511,7 +512,7 @@ public async Task ExportCompletes() private bool CheckMessage(Message message, ExportStatus exportStatus, FileExportStatus fileExportStatus) { - Guard.Against.Null(message); + Guard.Against.Null(message, nameof(message)); var exportEvent = message.ConvertTo(); return exportEvent.Status == exportStatus && @@ -538,7 +539,7 @@ private async Task StopAndVerify(ScuExportService service) { await service.StopAsync(_cancellationTokenSource.Token); _logger.VerifyLogging($"{service.ServiceName} is stopping.", LogLevel.Information, Times.Once()); - Thread.Sleep(500); + await Task.Delay(500); } } } diff --git a/src/InformaticsGateway/Test/Services/Fhir/FhirJsonReaderTest.cs b/src/InformaticsGateway/Test/Services/Fhir/FhirJsonReaderTest.cs index 42e10e3f6..7bb377917 100644 --- a/src/InformaticsGateway/Test/Services/Fhir/FhirJsonReaderTest.cs +++ b/src/InformaticsGateway/Test/Services/Fhir/FhirJsonReaderTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ using System.IO; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; +using System.Net; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -52,7 +53,7 @@ public FhirJsonReaderTest() [Fact] public async Task GetContentAsync_WhenCalled_EnsuresArgumentsAreValid() { - var request = new Mock(); + var request = CreateHttpRquestMock(); var correlationId = Guid.NewGuid().ToString(); var resourceType = "Patient"; var reader = new FhirJsonReader(_logger.Object, _options, _fileSystem); @@ -67,7 +68,7 @@ public async Task GetContentAsync_WhenCalled_EnsuresArgumentsAreValid() [Fact] public async Task GetContentAsync_WhenCalledWithEmptyContent_ThrowsException() { - var request = new Mock(); + var request = CreateHttpRquestMock(); var correlationId = Guid.NewGuid().ToString(); var resourceType = "Patient"; var contentType = new MediaTypeHeaderValue(ContentTypes.ApplicationFhirJson); @@ -88,7 +89,7 @@ await Assert.ThrowsAnyAsync(async () => [Fact] public async Task GetContentAsync_WhenCalledWithNonXmlContent_ThrowsException() { - var request = new Mock(); + var request = CreateHttpRquestMock(); var correlationId = Guid.NewGuid().ToString(); var resourceType = "Patient"; var contentType = new MediaTypeHeaderValue(ContentTypes.ApplicationFhirJson); @@ -111,7 +112,8 @@ await Assert.ThrowsAnyAsync(async () => [InlineData("{\"resourceType\":\"Patient\",\"id\":\" \",\"name\":[{\"use\":\"official\",\"family\":\"Monai\",\"given\":[\"Deploy\"]}]}")] public async Task GetContentAsync_WhenCalledWithNoId_ReturnsOriginalWithId(string xml) { - var request = new Mock(); + var request = CreateHttpRquestMock(); + var correlationId = Guid.NewGuid().ToString(); var resourceType = "Patient"; var contentType = new MediaTypeHeaderValue(ContentTypes.ApplicationFhirJson); @@ -131,5 +133,16 @@ public async Task GetContentAsync_WhenCalledWithNoId_ReturnsOriginalWithId(strin Assert.NotNull(results.Metadata); Assert.Contains($"\"id\": \"{correlationId}\"", results.RawData); } + + private Mock CreateHttpRquestMock() + { + var request = new Mock(); + var context = new Mock(); + var connection = new Mock(); + request.Setup(p => p.HttpContext).Returns(context.Object); + context.Setup(p => p.Connection).Returns(connection.Object); + connection.Setup(p => p.RemoteIpAddress).Returns(new IPAddress(16885952)); + return request; + } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/Fhir/FhirServiceTest.cs b/src/InformaticsGateway/Test/Services/Fhir/FhirServiceTest.cs index c4ccb736d..827e1605c 100644 --- a/src/InformaticsGateway/Test/Services/Fhir/FhirServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Fhir/FhirServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ using System; using System.IO; using System.IO.Abstractions; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -30,6 +31,7 @@ using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Fhir; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.Messaging.Events; using Moq; using xRetry; using Xunit; @@ -67,6 +69,11 @@ public FhirServiceTest() _fileSystem = new Mock(); _httpRequest = new Mock(); + var context = new Mock(); + var connection = new Mock(); + _httpRequest.Setup(p => p.HttpContext).Returns(context.Object); + context.Setup(p => p.Connection).Returns(connection.Object); + connection.Setup(p => p.RemoteIpAddress).Returns(new IPAddress(16885952)); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); @@ -85,7 +92,7 @@ public FhirServiceTest() _options.Value.Storage.TemporaryDataStorage = TemporaryDataStorageLocation.Memory; } - [RetryFact(10,200)] + [RetryFact(10, 200)] public void GivenAFhirService_WhenInitialized_ExpectParametersToBeValidated() { Assert.Throws(() => new FhirService(null, null)); @@ -94,7 +101,7 @@ public void GivenAFhirService_WhenInitialized_ExpectParametersToBeValidated() new FhirService(_serviceScopeFactory.Object, _options); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public async Task StoreAsync_WhenCalled_ShallValidateParametersAsync() { var correlationId = Guid.NewGuid().ToString(); @@ -151,7 +158,7 @@ public async Task StoreAsync_WhenCalledWithValidContent_ShallQueueForProessing(s Assert.Equal(StatusCodes.Status201Created, results.StatusCode); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Once()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/Fhir/FhirXmlReaderTest.cs b/src/InformaticsGateway/Test/Services/Fhir/FhirXmlReaderTest.cs index c01dd00cc..e0b6a27f2 100644 --- a/src/InformaticsGateway/Test/Services/Fhir/FhirXmlReaderTest.cs +++ b/src/InformaticsGateway/Test/Services/Fhir/FhirXmlReaderTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ using System.IO; using System.IO.Abstractions; using System.IO.Abstractions.TestingHelpers; +using System.Net; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -128,7 +129,7 @@ await Assert.ThrowsAsync(async () => [InlineData("")] public async Task GetContentAsync_WhenCalledWithNoId_ReturnsOriginalWithId(string xml) { - var request = new Mock(); + var request = CreateHttpRquestMock(); var correlationId = Guid.NewGuid().ToString(); var resourceType = "Patient"; var contentType = new MediaTypeHeaderValue(ContentTypes.ApplicationFhirXml); @@ -148,5 +149,16 @@ public async Task GetContentAsync_WhenCalledWithNoId_ReturnsOriginalWithId(strin Assert.NotNull(results.Metadata); Assert.Contains($"", results.RawData); } + + private Mock CreateHttpRquestMock() + { + var request = new Mock(); + var context = new Mock(); + var connection = new Mock(); + request.Setup(p => p.HttpContext).Returns(context.Object); + context.Setup(p => p.Connection).Returns(connection.Object); + connection.Setup(p => p.RemoteIpAddress).Returns(new IPAddress(16885952)); + return request; + } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs new file mode 100755 index 000000000..c9fba7e67 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllPExtractTests.cs @@ -0,0 +1,204 @@ + +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +using System.Threading; +using Moq; +using Xunit; +using Microsoft.Extensions.Logging; +using System; +using Monai.Deploy.InformaticsGateway.Api; +using System.Collections.Generic; +using HL7.Dotnetcore; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Api.Mllp; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using System.Threading.Tasks; +using Monai.Deploy.InformaticsGateway.Api.Models; +using FellowOakDicom; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.HealthLevel7 +{ + public class MllPExtractTests + { + private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r\n"; + private const string ABCDEMessage = "MSH|^~\\&|Rayvolve|ABCDE|RIS|{InstitutionName}|{YYYYMMDDHHMMSS}||ORU^R01|{UniqueIdentifier}|P|2.5\r\nPID|{StudyInstanceUID}|{AccessionNumber}\r\nOBR|{StudyInstanceUID}||{AccessionNumber}|Rayvolve^{AlgorithmUsed}||||||||||||{AccessionNumber}|||||||F||{PriorityValues, ex: A^ASAP^HL70078}\r\nTQ1|||||||||{PriorityValues, ex: A^ASAP^HL70078}\r\nOBX|1|ST|113014^DICOM Study^DCM||{StudyInstanceUID}||||||O\r\nOBX|2|TX|59776-5^Procedure Findings^LN||{Textual findingsm, ex:\"Fracture detected\")}|||{Abnormal flag, ex : A^Abnormal^HL70078}|||F||||{ACR flag, ex : RID49482^Category 3 Non critical Actionable Finding^RadLex}\r\n"; + private const string VendorMessage = "MSH|^~\\&|Vendor INSIGHT CXR |Vendor Inc.|||20231130091315||ORU^R01|ORU20231130091315834|P|2.4||||||UNICODE UTF-8\r\nPID|1||2.25.82866891564990604580806081805518233357\r\nPV1|1|O\r\nORC|RE||||SC\r\nOBR|1|||CXR0001^Chest X-ray Report|||20230418142212.134||||||||||||||||||P|||||||Vendor\r\nNTE|1||Bilateral lungs are clear without remarkable opacity.\\X0A\\Cardiomediastinal contour appears normal.\\X0A\\Pneumothorax is not seen.\\X0A\\Pleural Effusion is present on the bilateral sides.\\X0A\\\\X0A\\Threshold value\\X0A\\Atelectasis: 15\\X0A\\Calcification: 15\\X0A\\Cardiomegaly: 15\\X0A\\Consolidation: 15\\X0A\\Fibrosis: 15\\X0A\\Mediastinal Widening: 15\\X0A\\Nodule: 15\\X0A\\Pleural Effusion: 15\\X0A\\Pneumoperitoneum: 15\\X0A\\Pneumothorax: 15\\X0A\\\r\nZDS|2.25.97606619386020057921123852316852071139||2.25.337759261491022538565548360794987622189|Vendor INSIGHT CXR|v3.1.5.3\r\nOBX|1|NM|RAB0001^Abnormality Score||50.82||||||P|||20230418142212.134||Vendor"; + + private readonly Mock> _logger; + private readonly CancellationTokenSource _cancellationTokenSource; + private readonly MllpExtract _sut; + private readonly Mock _l7ApplicationConfigRepository = new Mock(); + private readonly Mock _externalAppDetailsRepository = new Mock(); + + public MllPExtractTests() + { + _logger = new Mock>(); + _cancellationTokenSource = new CancellationTokenSource(); + _sut = new MllpExtract(_l7ApplicationConfigRepository.Object, _externalAppDetailsRepository.Object, _logger.Object); + } + + [Fact(DisplayName = "Constructor Should Throw on missing arguments")] + public void Constructor_Should_Throw_on_missing_arguments() + { + Assert.Throws(() => new MllpExtract(null, null, null)); + Assert.Throws(() => new MllpExtract(_l7ApplicationConfigRepository.Object, null, null)); + Assert.Throws(() => new MllpExtract(_l7ApplicationConfigRepository.Object, _externalAppDetailsRepository.Object, null)); + + new MllpExtract(_l7ApplicationConfigRepository.Object, _externalAppDetailsRepository.Object, _logger.Object); + } + + [Fact(DisplayName = "ParseConfig Should Return Correct Item")] + public void ParseConfig_Should_Return_Correct_Item() + { + var correctid = new Guid("00000000-0000-0000-0000-000000000002"); + var azCorrectid = new Guid("00000000-0000-0000-0000-000000000001"); + var configs = new List { + new Hl7ApplicationConfigEntity{ Id= new Guid("00000000-0000-0000-0000-000000000001"), SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "ABCDE" } }, + new Hl7ApplicationConfigEntity{ Id= correctid, SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "MD HOSPITAL" } }, + }; + + var message = new Message(SampleMessage); + var isParsed = message.ParseMessage(); + + var config = _sut.GetConfig(configs, message); + Assert.Equal(correctid, config?.Id); + + message = new Message(ABCDEMessage); + isParsed = message.ParseMessage(); + + config = _sut.GetConfig(configs, message); + Assert.Equal(azCorrectid, config?.Id); + } + + [Fact(DisplayName = "Should Set MetaData On Hl7FileStorageMetadata Object")] + public async Task Should_Set_MetaData_On_Hl7FileStorageMetadata_Object() + { + var correctid = new Guid("00000000-0000-0000-0000-000000000002"); + var azCorrectid = new Guid("00000000-0000-0000-0000-000000000001"); + var configs = new List { + new Hl7ApplicationConfigEntity{ + Id= new Guid("00000000-0000-0000-0000-000000000001"), + SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "ABCDE" } + ,DataLink = new DataKeyValuePair{ Key = "PID.1", Value = DataLinkType.StudyInstanceUid } + }, + new Hl7ApplicationConfigEntity{ Id= correctid, SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "MD HOSPITAL" } }, + }; + + _l7ApplicationConfigRepository + .Setup(x => x.GetAllAsync(new CancellationToken())) + .ReturnsAsync(configs); + + _externalAppDetailsRepository.Setup(x => x.GetByStudyIdOutboundAsync("{StudyInstanceUID}", It.IsAny())) + .ReturnsAsync(new ExternalAppDetails + { + WorkflowInstanceId = "WorkflowInstanceId2", + ExportTaskID = "ExportTaskID2", + CorrelationId = "CorrelationId2", + DestinationFolder = "DestinationFolder2" + }); + + var message = new Message(ABCDEMessage); + var isParsed = message.ParseMessage(); + + var meatData = new Hl7FileStorageMetadata { Id = "metaId", File = new StorageObjectMetadata("txt") }; + + var configItem = await _sut.GetConfigItem(message); + await _sut.ExtractInfo(meatData, message, configItem); + + Assert.Equal("WorkflowInstanceId2", meatData.WorkflowInstanceId); + Assert.Equal("ExportTaskID2", meatData.TaskId); + Assert.Equal("CorrelationId2", meatData.CorrelationId); + Assert.StartsWith("DestinationFolder2", meatData.File.UploadPath); + } + + [Fact(DisplayName = "Should Set Original Patient And Study Uid")] + public async Task Should_Set_Original_Patient_And_Study_Uid() + { + var correctid = new Guid("00000000-0000-0000-0000-000000000002"); + var azCorrectid = new Guid("00000000-0000-0000-0000-000000000001"); + var configs = new List { + new Hl7ApplicationConfigEntity{ + Id= new Guid("00000000-0000-0000-0000-000000000001"), + SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "ABCDE" } + ,DataLink = new DataKeyValuePair{ Key = "PID.1", Value = DataLinkType.StudyInstanceUid }, + DataMapping = new List{ + new StringKeyValuePair { Key = "PID.1", Value = DicomTag.StudyInstanceUID.ToString() }, + new StringKeyValuePair { Key = "OBR.3", Value = DicomTag.PatientID.ToString() }, + } + + }, + new Hl7ApplicationConfigEntity{ Id= correctid, SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "MD HOSPITAL" } }, + }; + + _l7ApplicationConfigRepository + .Setup(x => x.GetAllAsync(new CancellationToken())) + .ReturnsAsync(configs); + + _externalAppDetailsRepository.Setup(x => x.GetByStudyIdOutboundAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new ExternalAppDetails + { + WorkflowInstanceId = "WorkflowInstanceId2", + ExportTaskID = "ExportTaskID2", + CorrelationId = "CorrelationId2", + DestinationFolder = "DestinationFolder2", + PatientId = "PatentID", + StudyInstanceUid = "StudyInstanceId" + }); + + var message = new Message(ABCDEMessage); + var isParsed = message.ParseMessage(); + + var te = message.GetValue("OBR.1"); + + var meatData = new Hl7FileStorageMetadata { Id = "metaId", File = new StorageObjectMetadata("txt") }; + + var configItem = await _sut.GetConfigItem(message); + message = await _sut.ExtractInfo(meatData, message, configItem); + + Assert.Equal("PatentID", message.GetValue("OBR.3")); + Assert.Equal("PatentID", message.GetValue("PID.2")); + Assert.Equal("StudyInstanceId", message.GetValue("PID.1")); + Assert.Equal("StudyInstanceId", message.GetValue("OBR.1")); + + } + + [Fact(DisplayName = "ParseConfig Should Return Correct Item for vendor")] + public void ParseConfig_Should_Return_Correct_Item_For_Vendor() + { + var correctid = new Guid("00000000-0000-0000-0000-000000000002"); + var azCorrectid = new Guid("00000000-0000-0000-0000-000000000001"); + + var configs = new List { + new Hl7ApplicationConfigEntity{ Id= new Guid("00000000-0000-0000-0000-000000000001"), SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "ABCDE" } }, + new Hl7ApplicationConfigEntity{ Id= correctid, SendingId = new StringKeyValuePair{ Key = "MSH.4", Value = "Vendor Inc." } }, + }; + + var message = new Message(VendorMessage); + var isParsed = message.ParseMessage(); + + var config = _sut.GetConfig(configs, message); + Assert.Equal(correctid, config?.Id); + + message = new Message(ABCDEMessage); + isParsed = message.ParseMessage(); + + config = _sut.GetConfig(configs, message); + Assert.Equal(azCorrectid, config?.Id); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs old mode 100644 new mode 100755 index f9900619c..8c8b3fa68 --- a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpClientTest.cs @@ -22,6 +22,7 @@ using System.Threading.Tasks; using HL7.Dotnetcore; using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; @@ -32,12 +33,13 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.HealthLevel7 { public class MllpClientTest { - private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r\n"; + private const string SampleMessage = "MSH|^~\\&|MD|MD HOSPITAL|MD Test|MONAI Deploy|202207130000|SECURITY|MD^A01^ADT_A01|MSG00001|P|2.8||||\r"; private readonly Mock _tcpClient; private readonly Hl7Configuration _config; private readonly Mock> _logger; private readonly CancellationTokenSource _cancellationTokenSource; + private readonly Mock _mIIpExtract = new Mock(); public MllpClientTest() { @@ -55,6 +57,7 @@ public void Constructor() Assert.Throws(() => new MllpClient(null, null, null)); Assert.Throws(() => new MllpClient(_tcpClient.Object, null, null)); Assert.Throws(() => new MllpClient(_tcpClient.Object, _config, null)); + Assert.Throws(() => new MllpClient(_tcpClient.Object, _config, null)); new MllpClient(_tcpClient.Object, _config, _logger.Object); } @@ -131,10 +134,8 @@ public async Task ReceiveData_InvalidMessage() { await Task.Run(() => { - Assert.Empty(results.Messages); Assert.NotNull(results.AggregateException); - Assert.Single(results.AggregateException.InnerExceptions); - Assert.Contains("Failed to validate the message with error", results.AggregateException.InnerExceptions.First().Message); + Assert.Equal(2, results.AggregateException.InnerExceptions.Count); }); }); await client.Start(action, _cancellationTokenSource.Token); diff --git a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs old mode 100644 new mode 100755 index bdaaf3934..6dea33186 --- a/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/HealthLevel7/MllpServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,11 @@ using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; using System.Net; using System.Threading; using System.Threading.Tasks; +using HL7.Dotnetcore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -29,12 +29,16 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Connectors; -using Monai.Deploy.InformaticsGateway.Services.HealthLevel7; +using Monai.Deploy.InformaticsGateway.Api.Mllp; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; using Moq; using xRetry; using Xunit; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; namespace Monai.Deploy.InformaticsGateway.Test.Services.HealthLevel7 { @@ -52,9 +56,12 @@ public class MllpServiceTest private readonly Mock _fileSystem; private readonly CancellationTokenSource _cancellationTokenSource; private readonly Mock _serviceScope; - private readonly Mock> _logger; + private readonly Mock> _logger; private readonly IServiceProvider _serviceProvider; private readonly Mock _storageInfoProvider; + private readonly Mock _mIIpExtract = new Mock(); + private readonly Mock _hl7DataPlugInEngine = new Mock(); + private readonly Mock _hl7ApplicationConfigRepository = new Mock(); public MllpServiceTest() { @@ -72,7 +79,7 @@ public MllpServiceTest() _cancellationTokenSource = new CancellationTokenSource(); _serviceScope = new Mock(); - _logger = new Mock>(); + _logger = new Mock>(); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); @@ -84,33 +91,34 @@ public MllpServiceTest() services.AddScoped(p => _payloadAssembler.Object); services.AddScoped(p => _fileSystem.Object); services.AddScoped(p => _storageInfoProvider.Object); + services.AddScoped(p => _mIIpExtract.Object); + services.AddScoped(p => _hl7DataPlugInEngine.Object); + services.AddScoped(p => _hl7ApplicationConfigRepository.Object); _serviceProvider = services.BuildServiceProvider(); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _fileSystem.Setup(p => p.Path.Combine(It.IsAny(), It.IsAny())).Returns((string path1, string path2) => System.IO.Path.Combine(path1, path2)); - _fileSystem.Setup(p => p.File.Create(It.IsAny())).Returns(FileStream.Null); - _loggerFactory.Setup(p => p.CreateLogger(It.IsAny())).Returns(_logger.Object); _tcpListenerFactory.Setup(p => p.CreateTcpListener(It.IsAny(), It.IsAny())).Returns(_tcpListener.Object); _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); _storageInfoProvider.Setup(p => p.HasSpaceAvailableToStore).Returns(true); + _options.Value.Storage.TemporaryDataStorage = TemporaryDataStorageLocation.Memory; } [RetryFact(10, 250)] public void GivenAMllpService_WhenInitialized_ExpectParametersToBeValidated() { - Assert.Throws(() => new MllpService(null, null)); - Assert.Throws(() => new MllpService(_serviceScopeFactory.Object, null)); + Assert.Throws(() => new MllpServiceHost(null, null)); + Assert.Throws(() => new MllpServiceHost(_serviceScopeFactory.Object, null)); - new MllpService(_serviceScopeFactory.Object, _options); + new MllpServiceHost(_serviceScopeFactory.Object, _options); } [RetryFact(5, 250)] public void GivenAMllpService_WhenStartAsyncIsCalled_ExpectServiceStartupNormally() { - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); var task = service.StartAsync(_cancellationTokenSource.Token); Assert.NotNull(task); @@ -121,7 +129,7 @@ public void GivenAMllpService_WhenStartAsyncIsCalled_ExpectServiceStartupNormall public void GivenAMllpService_WhenStopAsyncIsCalled_ExpectServiceStopsNormally() { _tcpListener.Setup(p => p.Stop()); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); var task = service.StopAsync(_cancellationTokenSource.Token); Assert.NotNull(task); @@ -130,7 +138,7 @@ public void GivenAMllpService_WhenStopAsyncIsCalled_ExpectServiceStopsNormally() } [RetryFact(10, 100)] - public void GivenTcpConnections_WhenConnectsAndDisconnectsFromMllpService_ExpectItToTrackActiveConnections() + public async Task GivenTcpConnections_WhenConnectsAndDisconnectsFromMllpService_ExpectItToTrackActiveConnections() { var actions = new Dictionary>(); var mllpClients = new List>(); @@ -160,26 +168,26 @@ public void GivenTcpConnections_WhenConnectsAndDisconnectsFromMllpService_Expect while (true) { - Thread.Sleep(100); + Task.Delay(100).GetAwaiter().GetResult(); } }); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); _ = service.StartAsync(_cancellationTokenSource.Token); Assert.True(checkEvent.Wait(3000)); - Thread.Sleep(200); + await Task.Delay(200); Assert.Equal(checkEvent.InitialCount, service.ActiveConnections); foreach (var action in actions.Keys) { - actions[action](action, new MllpClientResult(null, null)); + await actions[action](action, new MllpClientResult(null, null)); } Assert.Equal(0, service.ActiveConnections); } - [RetryFact(10,250)] - public void GivenAMllpService_WhenMaximumConnectionLimitIsConfigure_ExpectTheServiceToAbideByTheLimit() + [RetryFact(10, 250)] + public async Task GivenAMllpService_WhenMaximumConnectionLimitIsConfigure_ExpectTheServiceToAbideByTheLimit() { var checkEvent = new CountdownEvent(_options.Value.Hl7.MaximumNumberOfConnections); var mllpClients = new List>(); @@ -200,18 +208,18 @@ public void GivenAMllpService_WhenMaximumConnectionLimitIsConfigure_ExpectTheSer _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) .Returns(ValueTask.FromResult((new Mock()).Object)); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); _ = service.StartAsync(_cancellationTokenSource.Token); checkEvent.Wait(); - Thread.Sleep(500); + await Task.Delay(500); Assert.Equal(_options.Value.Hl7.MaximumNumberOfConnections, service.ActiveConnections); _tcpListener.Verify(p => p.AcceptTcpClientAsync(It.IsAny()), Times.Exactly(_options.Value.Hl7.MaximumNumberOfConnections)); _logger.VerifyLoggingMessageBeginsWith($"Maximum number {_options.Value.Hl7.MaximumNumberOfConnections} of clients reached.", LogLevel.Information, Times.AtLeastOnce()); } - [RetryFact(10,250)] + [RetryFact(10, 250)] public async Task GivenConnectedTcpClients_WhenDisconnects_ExpectServiceToDisposeResources() { var checkEvent = new ManualResetEventSlim(); @@ -234,21 +242,21 @@ public async Task GivenConnectedTcpClients_WhenDisconnects_ExpectServiceToDispos _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) .Returns(ValueTask.FromResult((new Mock()).Object)); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); _ = service.StartAsync(_cancellationTokenSource.Token); Assert.True(checkEvent.Wait(3000)); - await Task.Delay(200).ConfigureAwait(false); + await Task.Delay(200).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); Assert.True(service.ActiveConnections > 0); _cancellationTokenSource.Cancel(); - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(500).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); service.Dispose(); client.Verify(p => p.Dispose(), Times.Exactly(callCount)); } - [RetryFact(10,250)] + [RetryFact(10, 250)] public async Task GivenATcpClientWithHl7Messages_WhenStorageSpaceIsLow_ExpectToDisconnect() { _storageInfoProvider.Setup(p => p.HasSpaceAvailableToStore).Returns(false); @@ -259,15 +267,15 @@ public async Task GivenATcpClientWithHl7Messages_WhenStorageSpaceIsLow_ExpectToD _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) .Returns(ValueTask.FromResult(clientAdapter.Object)); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); _ = service.StartAsync(_cancellationTokenSource.Token); _cancellationTokenSource.CancelAfter(400); - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(500).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); clientAdapter.Verify(p => p.Close(), Times.AtLeastOnce()); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); } [RetryFact(10, 250)] @@ -275,6 +283,9 @@ public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageT { var checkEvent = new ManualResetEventSlim(); var client = new Mock(); + _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg, Hl7ApplicationConfigEntity configItem) => Msg); + _mllpClientFactory.Setup(p => p.CreateClient(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns(() => { @@ -300,14 +311,104 @@ public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageT _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) .Returns(ValueTask.FromResult((new Mock()).Object)); - var service = new MllpService(_serviceScopeFactory.Object, _options); + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); _ = service.StartAsync(_cancellationTokenSource.Token); Assert.True(checkEvent.Wait(3000)); - await Task.Delay(500).ConfigureAwait(false); + await Task.Delay(500).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Exactly(3)); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny()), Times.Exactly(3)); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(3)); + } + + [RetryFact(10, 250)] + public async Task GivenATcpClientWithHl7Messages_WhenDisconnected_ExpectMessageToBeRePopulated() + { + var checkEvent = new ManualResetEventSlim(); + var client = new Mock(); + + _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg, Hl7ApplicationConfigEntity configItem) => Msg); + + _mIIpExtract.Setup(e => e.GetConfigItem(It.IsAny())) + .ReturnsAsync((Message Msg) => new Hl7ApplicationConfigEntity()); + + _mllpClientFactory.Setup(p => p.CreateClient(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(() => + { + client.Setup(p => p.Start(It.IsAny>(), It.IsAny())) + .Callback, CancellationToken>((action, cancellationToken) => + { + var results = new MllpClientResult( + new List + { + new HL7.Dotnetcore.Message(""), + new HL7.Dotnetcore.Message(""), + new HL7.Dotnetcore.Message(""), + }, null); + action(client.Object, results); + checkEvent.Set(); + _cancellationTokenSource.Cancel(); + }); + client.Setup(p => p.Dispose()); + client.SetupGet(p => p.ClientId).Returns(Guid.NewGuid()); + return client.Object; + }); + + _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) + .Returns(ValueTask.FromResult((new Mock()).Object)); + + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); + _ = service.StartAsync(_cancellationTokenSource.Token); + + Assert.True(checkEvent.Wait(3000)); + await Task.Delay(500).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _mIIpExtract.Verify(p => p.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(3)); + } + + [RetryFact(10, 250)] + public async Task GivenATcpClientWithHl7Messages_ShouldntAdddBlankPlugin() + { + var checkEvent = new ManualResetEventSlim(); + var client = new Mock(); + _mIIpExtract.Setup(e => e.ExtractInfo(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync((Hl7FileStorageMetadata meta, Message Msg, Hl7ApplicationConfigEntity configItem) => Msg); + + _hl7ApplicationConfigRepository.Setup(p => p.GetAllAsync(It.IsAny())) + .ReturnsAsync(new List { new Hl7ApplicationConfigEntity { + PlugInAssemblies = [""] + } }); + + _mllpClientFactory.Setup(p => p.CreateClient(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(() => + { + client.Setup(p => p.Start(It.IsAny>(), It.IsAny())) + .Callback, CancellationToken>((action, cancellationToken) => + { + var results = new MllpClientResult( + new List + { + new("") + }, null); + action(client.Object, results); + checkEvent.Set(); + _cancellationTokenSource.Cancel(); + }); + client.Setup(p => p.Dispose()); + client.SetupGet(p => p.ClientId).Returns(Guid.NewGuid()); + return client.Object; + }); + + _tcpListener.Setup(p => p.AcceptTcpClientAsync(It.IsAny())) + .Returns(ValueTask.FromResult((new Mock()).Object)); + + var service = new MllpServiceHost(_serviceScopeFactory.Object, _options); + _ = service.StartAsync(_cancellationTokenSource.Token); + + Assert.True(checkEvent.Wait(3000)); + await Task.Delay(500).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + _hl7DataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Never()); } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/Test/Services/Http/DestinationAeTitleControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/DestinationAeTitleControllerTest.cs old mode 100644 new mode 100755 index c695b6068..297c7362a --- a/src/InformaticsGateway/Test/Services/Http/DestinationAeTitleControllerTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/DestinationAeTitleControllerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,18 @@ using System.Linq; using System.Linq.Expressions; using System.Net; +using System.Security.Claims; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Http; using Monai.Deploy.InformaticsGateway.Services.Scu; using Moq; @@ -37,11 +41,13 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Http { public class DestinationAeTitleControllerTest { + private static readonly string TestUsername = "test-user"; private readonly DestinationAeTitleController _controller; private readonly Mock _problemDetailsFactory; private readonly Mock> _logger; private readonly Mock _scuQueue; private readonly Mock _repository; + private readonly Mock> _pluginFactory; public DestinationAeTitleControllerTest() { @@ -70,15 +76,17 @@ public DestinationAeTitleControllerTest() }); _repository = new Mock(); + _pluginFactory = new Mock>(); var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; _controller = new DestinationAeTitleController( _logger.Object, _repository.Object, - _scuQueue.Object) + _scuQueue.Object, + _pluginFactory.Object) { ProblemDetailsFactory = _problemDetailsFactory.Object, - ControllerContext = controllerContext + ControllerContext = controllerContext, }; } @@ -403,6 +411,49 @@ public async Task Update_ReturnsUpdated() Assert.Equal(entity.HostIp, updatedEntity.HostIp); Assert.Equal(entity.Name, updatedEntity.Name); Assert.Equal(entity.Port, updatedEntity.Port); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Null(updatedEntity.UpdatedBy); + + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return updated when user is authenticated")] + public async Task Update_ReturnsUpdatedWhenUserIsAuthenticated() + { + var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() { User = new ClaimsPrincipal(new GenericIdentity(TestUsername)) } }; + var controller = new DestinationAeTitleController( + _logger.Object, + _repository.Object, + _scuQueue.Object, + _pluginFactory.Object) + { + ProblemDetailsFactory = _problemDetailsFactory.Object, + ControllerContext = controllerContext, + }; + + var entity = new DestinationApplicationEntity + { + AeTitle = "AET", + HostIp = "host", + Name = "AET", + Port = 123, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as DestinationApplicationEntity; + + Assert.Equal(entity.AeTitle, updatedEntity.AeTitle); + Assert.Equal(entity.HostIp, updatedEntity.HostIp); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.Equal(entity.Port, updatedEntity.Port); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); @@ -547,5 +598,41 @@ public async Task Delete_ShallReturnProblemOnFailure() } #endregion Delete + + #region GetPlugIns + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return registered plug-ins")] + public void GetPlugIns_ReturnsRegisteredPlugIns() + { + var input = new Dictionary() { { "A", "1" }, { "B", "3" }, { "C", "3" } }; + + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Returns(input); + + var result = _controller.GetPlugIns(); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as IDictionary; + Assert.NotNull(response); + Assert.Equal(input, response); + + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return problem on failure")] + public void GetPlugIns_ShallReturnProblemOnFailure() + { + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Throws(new Exception("error")); + + var result = _controller.GetPlugIns(); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error reading data input plug-ins.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + #endregion GetPlugIns } } diff --git a/src/InformaticsGateway/Test/Services/Http/DicomAssociationInfoControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/DicomAssociationInfoControllerTest.cs new file mode 100755 index 000000000..cea575eef --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Http/DicomAssociationInfoControllerTest.cs @@ -0,0 +1,80 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Common.Pagination; +using Monai.Deploy.InformaticsGateway.Services.Http; +using Monai.Deploy.InformaticsGateway.Services.UriService; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Http +{ + public class DicomAssociationInfoControllerTest + { + private readonly Mock> _logger; + private readonly Mock _loggerFactory; + private readonly DicomAssociationInfoController _controller; + private readonly IOptions _options; + private readonly Mock _repo; + private readonly UriService _uriService; + + public DicomAssociationInfoControllerTest() + { + _loggerFactory = new Mock(); + _logger = new Mock>(); + _repo = new Mock(); + _loggerFactory.Setup(p => p.CreateLogger(It.IsAny())).Returns(_logger.Object); + _options = Options.Create(new HttpEndpointSettings()); + _uriService = new UriService(new Uri("https://test.com/")); + + _controller = new DicomAssociationInfoController(_logger.Object, _options, _repo.Object, _uriService); + } + + [Fact] + public async Task GetAllAsync_GiveExpectedInput_ReturnsOK() + { + var input = new TimeFilter + { + EndTime = DateTime.Now, + StartTime = DateTime.MinValue, + PageNumber = 0, + PageSize = 1 + }; + _repo.Setup(r => r.GetAllAsync(It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), It.IsAny())) + .ReturnsAsync(new List()); + var result = await _controller.GetAllAsync(input); + + var okResult = Assert.IsType(result); + var response = Assert.IsType>>(okResult.Value); + Assert.Equal(0, response.TotalRecords); + Assert.Empty(response.Data); + } + } +} diff --git a/src/InformaticsGateway/Test/Services/Http/DicomWeb/StowControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/DicomWeb/StowControllerTest.cs old mode 100644 new mode 100755 index 050af44bc..02a60a6de --- a/src/InformaticsGateway/Test/Services/Http/DicomWeb/StowControllerTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/DicomWeb/StowControllerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,6 @@ * limitations under the License. */ -using System; -using System.Threading; -using System.Threading.Tasks; using FellowOakDicom; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -28,6 +25,9 @@ using Monai.Deploy.InformaticsGateway.Services.Storage; using Moq; using Xunit; +using System.Threading.Tasks; +using System; +using System.Threading; namespace Monai.Deploy.InformaticsGateway.Test.Services.Http.DicomWeb { @@ -121,7 +121,7 @@ public async Task StoreInstances_ReturnsProblemDetailWhenStorageSpaceIsLow(strin [InlineData("workflow")] public async Task StoreInstances_ReturnsProblemDetailException(string workflow) { - _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception("error")); var result = await _controller.StoreInstances(workflow); @@ -142,7 +142,7 @@ public async Task StoreInstances_ReturnsProblemDetailException(string workflow) [InlineData("workflow")] public async Task StoreInstances_ReturnsOk(string workflow) { - _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new StowResult { StatusCode = StatusCodes.Status200OK, @@ -163,7 +163,7 @@ public async Task StoreInstances_ReturnsOk(string workflow) [InlineData("a", "workflow")] public async Task StoreInstancesToStudy_ReturnsProblemDetailWithInvalidUid(string studyInstanceUid, string workflow) { - _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new DicomValidationException("content", DicomVR.SL, "error")); var result = await _controller.StoreInstancesToStudy(studyInstanceUid, workflow); @@ -184,7 +184,7 @@ public async Task StoreInstancesToStudy_ReturnsProblemDetailWithInvalidUid(strin [InlineData("1.2.3.4.5", "workflow")] public async Task StoreInstancesToStudy_ReturnsProblemDetailException(string studyInstanceUid, string workflow) { - _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ThrowsAsync(new Exception("error")); var result = await _controller.StoreInstancesToStudy(studyInstanceUid, workflow); @@ -205,7 +205,7 @@ public async Task StoreInstancesToStudy_ReturnsProblemDetailException(string stu [InlineData("1.2.3.4.5", "workflow")] public async Task StoreInstancesToStudy_ReturnsOk(string studyInstanceUid, string workflow) { - _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + _stowService.Setup(p => p.StoreAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new StowResult { StatusCode = StatusCodes.Status200OK, diff --git a/src/InformaticsGateway/Test/Services/Http/Hl7ApplicationConfigControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/Hl7ApplicationConfigControllerTest.cs new file mode 100755 index 000000000..34dc2a2a8 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Http/Hl7ApplicationConfigControllerTest.cs @@ -0,0 +1,322 @@ +/* + * Copyright 2021-2023 MONAI Consortium + * Copyright 2019-2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Http; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Http +{ + public class Hl7ApplicationConfigControllerTest + { + private readonly Mock> _logger; + private readonly Mock _loggerFactory; + private readonly Hl7ApplicationConfigController _controller; + private readonly Mock _repo; + + public Hl7ApplicationConfigControllerTest() + { + _loggerFactory = new Mock(); + _logger = new Mock>(); + _repo = new Mock(); + _loggerFactory.Setup(p => p.CreateLogger(It.IsAny())).Returns(_logger.Object); + + _controller = new Hl7ApplicationConfigController( + _logger.Object, _repo.Object); + } + + private static Hl7ApplicationConfigEntity ValidApplicationEntity(string dicomStr) + { + var validApplicationEntity = new Hl7ApplicationConfigEntity() + { + Id = Guid.Empty, + DataLink = KeyValuePair.Create("testKey", DataLinkType.PatientId), + DataMapping = new() { KeyValuePair.Create("datamapkey", dicomStr) }, + SendingId = KeyValuePair.Create("sendingidkey", "sendingidvalue"), + DateTimeCreated = DateTime.UtcNow + }; + return validApplicationEntity; + } + + #region GET Tests + + [Fact] + public async Task Get_GiveExpectedInput_ReturnsOK() + { + _repo.Setup(r => r.GetAllAsync(It.IsAny())) + .ReturnsAsync(new List()); + var result = await _controller.Get(); + + var okResult = Assert.IsType(result); + var response = Assert.IsType>(okResult.Value); + Assert.Empty(response); + } + + [Fact] + public async Task Get_GiveExpectedInput_ReturnsNotFound() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync((Hl7ApplicationConfigEntity)null); + var result = await _controller.Get("test"); + + Assert.IsType(result); + } + + [Fact] + public async Task Get_GiveExpectedInput_ReturnsOK2() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + var result = await _controller.Get("test"); + + var okResult = Assert.IsType(result); + var response = Assert.IsType(okResult.Value); + Assert.NotNull(response); + } + #endregion + + #region DELETE Tests + + [Fact] + public async Task Delete_GiveExpectedInput_ReturnsNotFound() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync((Hl7ApplicationConfigEntity)null); + var result = await _controller.Delete("test"); + + Assert.IsType(result); + } + + [Fact] + public async Task Delete_GiveExpectedInput_ReturnsOK() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + _repo.Setup(r => r.DeleteAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + var result = await _controller.Delete("test"); + + var okResult = Assert.IsType(result); + var response = Assert.IsType(okResult.Value); + Assert.NotNull(response); + } + + [Fact] + public async Task Delete_GiveExpectedInput_ReturnsInternalServerError() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + _repo.Setup(r => r.DeleteAsync(It.IsAny(), It.IsAny())) + .Throws(new DatabaseException()); + var result = await _controller.Delete("test"); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + + [Fact] + public async Task Delete_GiveExpectedInput_ReturnsInternalServerError2() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + _repo.Setup(r => r.DeleteAsync(It.IsAny(), It.IsAny())) + .Throws(new Exception()); + var result = await _controller.Delete("test"); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status500InternalServerError, objectResult.StatusCode); + } + + #endregion + + #region PUT Tests + + [Fact] + public async Task Put_GiveExpectedInput_ReturnsOK() + { + var validApplicationEntity = ValidApplicationEntity("0001,0001"); + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.CreateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(validApplicationEntity); + var result = await _controller.Put(validApplicationEntity); + + var okResult = Assert.IsType(result); + var response = Assert.IsType(okResult.Value); + Assert.NotNull(response); + } + + [Fact] + public async Task Put_GiveExpectedInput_ReturnsBadRequest() + { + var result = await _controller.Put(null!); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + + [Fact] + public async Task Put_GiveExpectedInput_ReturnsBadRequest2() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(new Hl7ApplicationConfigEntity()); + var result = await _controller.Put(new Hl7ApplicationConfigEntity()); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + + [Fact] + public async Task Put_GiveExpectedInput_ReturnsInternalServerError() + { + var validApplicationEntity = ValidApplicationEntity("0001,0001"); + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.UpdateAsync(It.IsAny(), It.IsAny())) + .Throws(new DatabaseException()); + var result = await _controller.Put(validApplicationEntity); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status500InternalServerError, objectResult.StatusCode); + } + + #endregion + + #region POST Tests + + [Theory] + [InlineData("(0001,0001)")] + [InlineData("0001,0001")] + [InlineData("(FFFE,E0DD)")] + [InlineData("FFFE,E0DD")] + public async Task Post_GiveExpectedInput_ReturnsOK(string dicomStr) + { + var validApplicationEntity = ValidApplicationEntity(dicomStr); + + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.CreateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(validApplicationEntity); + var result = await _controller.Post(validApplicationEntity); + + var okResult = Assert.IsType(result); + var response = Assert.IsType(okResult.Value); + Assert.NotNull(response); + } + + [Theory] + [InlineData("(001,0001)")] + [InlineData("(0001,00x1")] + [InlineData("x001,0001)")] + [InlineData("00001,00001)")] + public async Task Post_GivenInvalidDicomValueInput_ReturnsBadRequest(string dicomStr) + { + //valid Hl7ApplicationEntity + var validApplicationEntity = new Hl7ApplicationConfigEntity() + { + Id = Guid.Empty, + DataLink = KeyValuePair.Create("testKey", DataLinkType.PatientId), + DataMapping = new() { KeyValuePair.Create("datamapkey", dicomStr) }, + SendingId = KeyValuePair.Create("sendingidkey", "sendingidvalue"), + DateTimeCreated = DateTime.UtcNow + }; + + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.UpdateAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(validApplicationEntity); + var result = await _controller.Post(validApplicationEntity); + + var objResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objResult.StatusCode); + } + + [Fact] + public async Task Post_GiveExpectedInput_ReturnsBadRequest() + { + var result = await _controller.Post(null!); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + + [Fact] + public async Task Post_GiveExpectedInput_ReturnsBadRequest2() + { + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync((Hl7ApplicationConfigEntity)null); + var result = await _controller.Post(new Hl7ApplicationConfigEntity()); + + var objectResult = Assert.IsType(result); + + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + + } + + [Fact] + public async Task Post_GiveExpectedInput_ReturnsInternalServerError() + { + var validApplicationEntity = ValidApplicationEntity("0001,0001"); + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.UpdateAsync(It.IsAny(), It.IsAny())) + .Throws(new DatabaseException()); + var result = await _controller.Post(validApplicationEntity); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + [Fact] + public async Task Post_GiveExpectedInput_ReturnsInternalServerError3() + { + var validApplicationEntity = ValidApplicationEntity("0001,0001"); + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + + var result = await _controller.Post(validApplicationEntity); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status400BadRequest, objectResult.StatusCode); + } + + [Fact] + public async Task Post_GiveExpectedInput_ReturnsInternalServerError2() + { + var validApplicationEntity = ValidApplicationEntity("0001,0001"); + _repo.Setup(r => r.GetByIdAsync(It.IsAny())) + .ReturnsAsync(validApplicationEntity); + _repo.Setup(r => r.CreateAsync(It.IsAny(), It.IsAny())) + .Throws(new Exception()); + var result = await _controller.Post(validApplicationEntity); + + var objectResult = Assert.IsType(result); + Assert.Equal(StatusCodes.Status500InternalServerError, objectResult.StatusCode); + } + + #endregion + } +} diff --git a/src/InformaticsGateway/Test/Services/Http/InferenceControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/InferenceControllerTest.cs index 8aadd13d8..5bc8d2458 100644 --- a/src/InformaticsGateway/Test/Services/Http/InferenceControllerTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/InferenceControllerTest.cs @@ -17,6 +17,8 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; +using System.Security.Claims; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -35,6 +37,7 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Http { public class InferenceControllerTest { + private static readonly string TestUsername = "test-user"; private readonly Mock _inferenceRequestRepository; private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; private readonly Mock> _logger; @@ -68,8 +71,8 @@ public InferenceControllerTest() Instance = instance }; }); - var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() { User = new ClaimsPrincipal(new GenericIdentity(TestUsername)) } }; _controller = new InferenceController(_inferenceRequestRepository.Object, _logger.Object) { ControllerContext = controllerContext, @@ -288,7 +291,7 @@ public void NewInferenceRequest_ShallAcceptInferenceRequest() var result = _controller.NewInferenceRequest(input); - _inferenceRequestRepository.Verify(p => p.AddAsync(input, It.IsAny()), Times.Once()); + _inferenceRequestRepository.Verify(p => p.AddAsync(It.Is(p => p.CreatedBy == TestUsername), It.IsAny()), Times.Once()); Assert.NotNull(result); var objectResult = result.Result as OkObjectResult; diff --git a/src/InformaticsGateway/Test/Services/Http/MonaiAeTitleControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/MonaiAeTitleControllerTest.cs old mode 100644 new mode 100755 index db8a80d93..1f2058d57 --- a/src/InformaticsGateway/Test/Services/Http/MonaiAeTitleControllerTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/MonaiAeTitleControllerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,18 @@ using System.Linq; using System.Linq.Expressions; using System.Net; +using System.Security.Claims; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Http; using Monai.Deploy.InformaticsGateway.Services.Scp; using Moq; @@ -37,11 +41,13 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Http { public class MonaiAeTitleControllerTest { + private static readonly string TestUsername = "test-user"; private readonly MonaiAeTitleController _controller; private readonly Mock _problemDetailsFactory; private readonly Mock> _logger; private readonly Mock _aeChangedNotificationService; private readonly Mock _repository; + private readonly Mock> _pluginFactory; public MonaiAeTitleControllerTest() { @@ -70,12 +76,14 @@ public MonaiAeTitleControllerTest() }); _repository = new Mock(); + _pluginFactory = new Mock>(); - var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() }; + var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() { User = new ClaimsPrincipal(new GenericIdentity(TestUsername)) } }; _controller = new MonaiAeTitleController( _logger.Object, _aeChangedNotificationService.Object, - _repository.Object) + _repository.Object, + _pluginFactory.Object) { ControllerContext = controllerContext, ProblemDetailsFactory = _problemDetailsFactory.Object @@ -181,7 +189,7 @@ public async Task GetAeTitle_ShallReturnProblemOnFailure() #region Create - [Fact(DisplayName = "Create - Shall return Conflict if entity already exists")] + [RetryFact(DisplayName = "Create - Shall return Conflict if entity already exists")] public async Task Create_ShallReturnConflictIfIEntityAlreadyExists() { _repository.Setup(p => p.ContainsAsync(It.IsAny>>(), It.IsAny())).ReturnsAsync(true); @@ -227,7 +235,7 @@ public async Task Create_ShallReturnBadRequestOnValidationFailure() Assert.Equal((int)HttpStatusCode.BadRequest, problem.Status); } - [RetryFact(DisplayName = "Create - Shall return BadRequest when both allowed & ignored SOP classes are defined")] + [Fact(DisplayName = "Create - Shall return BadRequest when both allowed & ignored SOP classes are defined")] public async Task Create_ShallReturnBadRequestWhenBothAllowedAndIgnoredSopsAreDefined() { _repository.Setup(p => p.ContainsAsync(It.IsAny>>(), It.IsAny())).ReturnsAsync(false); @@ -297,12 +305,191 @@ public async Task Create_ShallReturnCreatedAtAction() Assert.IsType(result.Result); - _aeChangedNotificationService.Verify(p => p.Notify(It.Is(x => x.ApplicationEntity == monaiAeTitle)), Times.Once()); - _repository.Verify(p => p.AddAsync(It.IsAny(), It.IsAny()), Times.Once()); + _aeChangedNotificationService.Verify(p => p.Notify(It.Is(x => x.ApplicationEntity == monaiAeTitle && x.Event == ChangedEventType.Added)), Times.Once()); + _repository.Verify(p => p.AddAsync(It.Is(p => p.CreatedBy == TestUsername), It.IsAny()), Times.Once()); } #endregion Create + #region Update + + [RetryFact(DisplayName = "Update - Shall return updated")] + public async Task Update_ReturnsUpdated() + { + var entity = new MonaiApplicationEntity + { + AeTitle = "AET", + Name = "AET", + Grouping = "0020,000E", + Timeout = 100, + Workflows = new List { "1", "2", "3" }, + AllowedSopClasses = new List { "1.2.3" }, + IgnoredSopClasses = new List { "a.b.c" }, + PlugInAssemblies = new List { "A", "B" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as MonaiApplicationEntity; + + Assert.Equal(entity.AeTitle, updatedEntity.AeTitle); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.Equal(entity.Grouping, updatedEntity.Grouping); + Assert.Equal(entity.Timeout, updatedEntity.Timeout); + Assert.Equal(entity.Workflows, updatedEntity.Workflows); + Assert.Equal(entity.AllowedSopClasses, updatedEntity.AllowedSopClasses); + Assert.Equal(entity.IgnoredSopClasses, updatedEntity.IgnoredSopClasses); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); + + _aeChangedNotificationService.Verify(p => p.Notify(It.Is(x => x.ApplicationEntity == updatedEntity && x.Event == ChangedEventType.Updated)), Times.Once()); + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); + } + + [RetryFact(DisplayName = "Update - Shall return updated without updating AE Title")] + public async Task Update_ReturnsUpdated_WithoutUpdatingAET() + { + var originalEntity = new MonaiApplicationEntity + { + AeTitle = "AET", + Name = "AET", + Grouping = "0020,000E", + Timeout = 100, + Workflows = new List { "1", "2", "3" }, + AllowedSopClasses = new List { "1.2.3" }, + IgnoredSopClasses = new List { "a.b.c" }, + }; + + var entity = new MonaiApplicationEntity + { + AeTitle = "SHOUD-NOT-CHANGE", + Name = "AET", + Grouping = "0020,000D", + Timeout = 10, + Workflows = new List { "1", "2" }, + AllowedSopClasses = new List { "1.2" }, + IgnoredSopClasses = new List { "a.b" }, + PlugInAssemblies = new List { "A", "B" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(originalEntity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as MonaiApplicationEntity; + + Assert.NotEqual(entity.AeTitle, updatedEntity.AeTitle); + Assert.Equal(originalEntity.AeTitle, updatedEntity.AeTitle); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.Equal(entity.Grouping, updatedEntity.Grouping); + Assert.Equal(entity.Timeout, updatedEntity.Timeout); + Assert.Equal(entity.Workflows, updatedEntity.Workflows); + Assert.Equal(entity.AllowedSopClasses, updatedEntity.AllowedSopClasses); + Assert.Equal(entity.IgnoredSopClasses, updatedEntity.IgnoredSopClasses); + Assert.Equal(entity.PlugInAssemblies, updatedEntity.PlugInAssemblies); + Assert.Collection(entity.PlugInAssemblies, p => p.Equals("A", StringComparison.CurrentCultureIgnoreCase), p => p.Equals("B", StringComparison.CurrentCultureIgnoreCase)); + + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); + + _aeChangedNotificationService.Verify(p => p.Notify(It.Is(x => x.ApplicationEntity == updatedEntity && x.Event == ChangedEventType.Updated)), Times.Once()); + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(updatedEntity, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return 404 if input is null")] + public async Task Update_Returns404IfInputIsNull() + { + var result = await _controller.Edit(null); + + Assert.IsType(result.Result); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return 404 if not found")] + public async Task Update_Returns404IfNotFound() + { + var entity = new MonaiApplicationEntity + { + AeTitle = "AET", + Name = "AET", + Grouping = "ABC", + Timeout = 100, + Workflows = new List { "1", "2", "3" }, + AllowedSopClasses = new List { "1.2.3" }, + IgnoredSopClasses = new List { "a.b.c" }, + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(default(MonaiApplicationEntity))); + + var result = await _controller.Edit(entity); + + Assert.IsType(result.Result); + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return problem on failure")] + public async Task Update_ShallReturnProblemOnFailure() + { + var value = "AET"; + var entity = new MonaiApplicationEntity + { + AeTitle = value, + Name = "AET", + Grouping = "0020,000E", + Timeout = 100, + Workflows = new List { "1", "2", "3" }, + AllowedSopClasses = new List { "1.2.3" }, + IgnoredSopClasses = new List { "a.b.c" }, + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.Edit(entity); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error updating MONAI Application Entity.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return problem on validation failure")] + public async Task Update_ShallReturnBadRequestWithBadAeTitle() + { + var aeTitle = "TOOOOOOOOOOOOOOOOOOOOOOOLONG"; + var entity = new MonaiApplicationEntity + { + AeTitle = aeTitle, + Name = "AET", + Grouping = "0020,000E", + Timeout = 100, + Workflows = new List { "1", "2", "3" }, + AllowedSopClasses = new List { "1.2.3" }, + IgnoredSopClasses = new List { "a.b.c" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + var result = await _controller.Edit(entity); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Validation error.", problem.Title); + Assert.Equal($"'{aeTitle}' is not a valid AE Title (source: MonaiApplicationEntity).", problem.Detail); + Assert.Equal((int)HttpStatusCode.BadRequest, problem.Status); + } + #endregion Update + #region Delete [RetryFact(5, 250, DisplayName = "Delete - Shall return deleted object")] @@ -323,6 +510,8 @@ public async Task Delete_ReturnsDeleted() var response = okObjectResult.Value as MonaiApplicationEntity; Assert.NotNull(response); Assert.Equal(value, response.AeTitle); + + _aeChangedNotificationService.Verify(p => p.Notify(It.Is(x => x.ApplicationEntity == response && x.Event == ChangedEventType.Deleted)), Times.Once()); _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); _repository.Verify(p => p.RemoveAsync(entity, It.IsAny()), Times.Once()); } @@ -364,5 +553,41 @@ public async Task Delete_ShallReturnProblemOnFailure() } #endregion Delete + + #region GetPlugIns + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return registered plug-ins")] + public void GetPlugIns_ReturnsRegisteredPlugIns() + { + var input = new Dictionary() { { "A", "1" }, { "B", "3" }, { "C", "3" } }; + + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Returns(input); + + var result = _controller.GetPlugIns(); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as IDictionary; + Assert.NotNull(response); + Assert.Equal(input, response); + + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return problem on failure")] + public void GetPlugIns_ShallReturnProblemOnFailure() + { + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Throws(new Exception("error")); + + var result = _controller.GetPlugIns(); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error reading data input plug-ins.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + #endregion GetPlugIns } } diff --git a/src/InformaticsGateway/Test/Services/Http/MonaiHealthCheckTest.cs b/src/InformaticsGateway/Test/Services/Http/MonaiHealthCheckTest.cs old mode 100644 new mode 100755 index ebf559a3f..f2548c1c7 --- a/src/InformaticsGateway/Test/Services/Http/MonaiHealthCheckTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/MonaiHealthCheckTest.cs @@ -14,12 +14,11 @@ * limitations under the License. */ -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Monai.Deploy.InformaticsGateway.Repositories; using Monai.Deploy.InformaticsGateway.Services.Http; using Moq; @@ -30,6 +29,7 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Http public class MonaiHealthCheckTest { private readonly Mock _monaiServiceLocator; + private readonly ILogger _logger = new NullLogger(); public MonaiHealthCheckTest() { @@ -46,7 +46,7 @@ public async Task GivenAllServicesRunning_WhenCheckHealthAsyncIsCalled_ReturnsHe { "C", Api.Rest.ServiceStatus.Running }, }); - var svc = new MonaiHealthCheck(_monaiServiceLocator.Object); + var svc = new MonaiHealthCheck(_monaiServiceLocator.Object, _logger); var result = await svc.CheckHealthAsync(null); Assert.Equal(HealthStatus.Healthy, result.Status); } @@ -61,7 +61,7 @@ public async Task GivenSomeServicesNotRunning_WhenCheckHealthAsyncIsCalled_Retur { "C", Api.Rest.ServiceStatus.Stopped }, }); - var svc = new MonaiHealthCheck(_monaiServiceLocator.Object); + var svc = new MonaiHealthCheck(_monaiServiceLocator.Object, _logger); var result = await svc.CheckHealthAsync(null); Assert.Equal(HealthStatus.Degraded, result.Status); Assert.Equal(Api.Rest.ServiceStatus.Cancelled, result.Data["B"]); @@ -78,7 +78,7 @@ public async Task GivenAllServicesNotRunning_WhenCheckHealthAsyncIsCalled_Return { "C", Api.Rest.ServiceStatus.Stopped }, }); - var svc = new MonaiHealthCheck(_monaiServiceLocator.Object); + var svc = new MonaiHealthCheck(_monaiServiceLocator.Object, _logger); var result = await svc.CheckHealthAsync(null); Assert.Equal(HealthStatus.Unhealthy, result.Status); diff --git a/src/InformaticsGateway/Test/Services/Http/SourceAeTitleControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/SourceAeTitleControllerTest.cs index 468a90eba..d03d95038 100644 --- a/src/InformaticsGateway/Test/Services/Http/SourceAeTitleControllerTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/SourceAeTitleControllerTest.cs @@ -19,6 +19,8 @@ using System.Linq; using System.Linq.Expressions; using System.Net; +using System.Security.Claims; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -36,6 +38,7 @@ namespace Monai.Deploy.InformaticsGateway.Test.Services.Http { public class SourceAeTitleControllerTest { + private static readonly string TestUsername = "test-user"; private readonly SourceAeTitleController _controller; private readonly Mock _problemDetailsFactory; private readonly Mock> _logger; @@ -142,6 +145,27 @@ public async Task GetAeTitle_ReturnsAMatch() _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); } + [RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return matching object")] + public async Task GetAeTitle_ViaAETitleReturnsAMatch() + { + var value = "AET"; + _repository.Setup(p => p.FindByAETAsync(It.IsAny(), It.IsAny())).Returns( + Task.FromResult( + new SourceApplicationEntity[]{ + new SourceApplicationEntity + { + AeTitle = value, + HostIp = "host", + Name = $"{value}name", + }})); + + var result = await _controller.GetAeTitleByAET(value); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as SourceApplicationEntity[]; + Assert.Equal(value, response.FirstOrDefault().AeTitle); + _repository.Verify(p => p.FindByAETAsync(value, It.IsAny()), Times.Once()); + } + [RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return 404 if not found")] public async Task GetAeTitle_Returns404IfNotFound() { @@ -172,6 +196,24 @@ public async Task GetAeTitle_ShallReturnProblemOnFailure() _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); } + [RetryFact(5, 250, DisplayName = "GetAeTitle from AETitle - Shall return problem on failure")] + public async Task GetAeTitleViaAETitle_ShallReturnProblemOnFailure() + { + var value = "AET"; + _repository.Setup(p => p.FindByAETAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.GetAeTitleByAET(value); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error querying DICOM sources.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _repository.Verify(p => p.FindByAETAsync(value, It.IsAny()), Times.Once()); + } + #endregion GetAeTitle #region Create @@ -295,6 +337,45 @@ public async Task Update_ReturnsUpdated() Assert.Equal(entity.AeTitle, updatedEntity.AeTitle); Assert.Equal(entity.HostIp, updatedEntity.HostIp); Assert.Equal(entity.Name, updatedEntity.Name); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Null(updatedEntity.UpdatedBy); + + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return updated when user is authenticated")] + public async Task Update_ReturnsUpdatedWhenUserIsAuthenticated() + { + var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() { User = new ClaimsPrincipal(new GenericIdentity(TestUsername)) } }; + var controller = new SourceAeTitleController( + _logger.Object, + _repository.Object) + { + ProblemDetailsFactory = _problemDetailsFactory.Object, + ControllerContext = controllerContext + }; + + var entity = new SourceApplicationEntity + { + AeTitle = "AET", + HostIp = "host", + Name = "AET", + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as SourceApplicationEntity; + + Assert.Equal(entity.AeTitle, updatedEntity.AeTitle); + Assert.Equal(entity.HostIp, updatedEntity.HostIp); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); diff --git a/src/InformaticsGateway/Test/Services/Http/StartupTest.cs b/src/InformaticsGateway/Test/Services/Http/StartupTest.cs index 6103c51a6..f5ad735bc 100644 --- a/src/InformaticsGateway/Test/Services/Http/StartupTest.cs +++ b/src/InformaticsGateway/Test/Services/Http/StartupTest.cs @@ -16,7 +16,9 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; using Monai.Deploy.InformaticsGateway.Services.Http; +using Monai.Deploy.Security.Authentication.Configurations; using Xunit; namespace Monai.Deploy.InformaticsGateway.Test.Services.Http @@ -27,7 +29,10 @@ public class StartupTest public async Task Startup_WebHostBuildsProperly() { var webHost = Microsoft.AspNetCore.WebHost.CreateDefaultBuilder() - .UseEnvironment("Development") + .ConfigureServices((hostContext, services) => + { + services.AddOptions().Bind(hostContext.Configuration.GetSection("MonaiDeployAuthentication")); + }) .UseStartup() .Build(); Assert.NotNull(webHost); diff --git a/src/InformaticsGateway/Test/Services/Http/VirtualAeTitleControllerTest.cs b/src/InformaticsGateway/Test/Services/Http/VirtualAeTitleControllerTest.cs new file mode 100644 index 000000000..fa7127b63 --- /dev/null +++ b/src/InformaticsGateway/Test/Services/Http/VirtualAeTitleControllerTest.cs @@ -0,0 +1,527 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Common; +using Monai.Deploy.InformaticsGateway.Services.Http; +using Moq; +using xRetry; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.Test.Services.Http +{ + public class VirtualAeTitleControllerTest + { + private static readonly string TestUsername = "test-user"; + private readonly VirtualAeTitleController _controller; + private readonly Mock _problemDetailsFactory; + private readonly Mock> _logger; + private readonly Mock _repository; + private readonly Mock> _pluginFactory; + + public VirtualAeTitleControllerTest() + { + _logger = new Mock>(); + + _problemDetailsFactory = new Mock(); + _problemDetailsFactory.Setup(_ => _.CreateProblemDetails( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny()) + ) + .Returns((HttpContext httpContext, int? statusCode, string title, string type, string detail, string instance) => + { + return new ProblemDetails + { + Status = statusCode, + Title = title, + Type = type, + Detail = detail, + Instance = instance + }; + }); + + _repository = new Mock(); + _pluginFactory = new Mock>(); + + var controllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() { User = new ClaimsPrincipal(new GenericIdentity(TestUsername)) } }; + _controller = new VirtualAeTitleController( + _logger.Object, + _repository.Object, + _pluginFactory.Object) + { + ControllerContext = controllerContext, + ProblemDetailsFactory = _problemDetailsFactory.Object + }; + } + + #region Get + + [RetryFact(5, 250, DisplayName = "Get - Shall return available MONAI AETs")] + public async Task Get_ShallReturnAllMonaiAets() + { + var data = new List(); + for (var i = 1; i <= 5; i++) + { + data.Add(new VirtualApplicationEntity() + { + VirtualAeTitle = $"AET{i}", + Name = $"AET{i}", + Workflows = new List { "A", "B" } + }); + } + + _repository.Setup(p => p.ToListAsync(It.IsAny())).Returns(Task.FromResult(data)); + + var result = await _controller.Get(); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as IEnumerable; + Assert.Equal(data.Count, response.Count()); + _repository.Verify(p => p.ToListAsync(It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Get - Shall return problem on failure")] + public async Task Get_ShallReturnProblemOnFailure() + { + _repository.Setup(p => p.ToListAsync(It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.Get(); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error querying database.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + } + + #endregion Get + + #region GetAeTitle + + [RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return matching object")] + public async Task GetAeTitle_ReturnsAMatch() + { + var value = "AET"; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns( + Task.FromResult( + new VirtualApplicationEntity + { + VirtualAeTitle = value, + Name = value, + Workflows = new List { "A", "B" } + })); + + var result = await _controller.GetAeTitle(value); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as VirtualApplicationEntity; + Assert.NotNull(response); + Assert.Equal(value, response.VirtualAeTitle); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return 404 if not found")] + public async Task GetAeTitle_Returns404IfNotFound() + { + var value = "AET"; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(default(VirtualApplicationEntity))); + + var result = await _controller.GetAeTitle(value); + + Assert.IsType(result.Result); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "GetAeTitle - Shall return problem on failure")] + public async Task GetAeTitle_ShallReturnProblemOnFailure() + { + var value = "AET"; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.GetAeTitle(value); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error querying Virtual Application Entity.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + #endregion GetAeTitle + + #region Create + + [RetryFact(DisplayName = "Create - Shall return Conflict if entity already exists")] + public async Task Create_ShallReturnConflictIfIEntityAlreadyExists() + { + _repository.Setup(p => p.ContainsAsync(It.IsAny>>(), It.IsAny())).ReturnsAsync(true); + + var virtualAeTitle = new VirtualApplicationEntity + { + Name = "AET1", + VirtualAeTitle = "AET1", + Workflows = new List { "A", "B" } + }; + + var result = await _controller.Create(virtualAeTitle); + + Assert.NotNull(result); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("AE Title already exists", problem.Title); + Assert.Equal("A Virtual Application Entity with the same name 'AET1' already exists.", problem.Detail); + Assert.Equal((int)HttpStatusCode.Conflict, problem.Status); + } + + [Fact(DisplayName = "Create - Shall return BadRequest when validation fails")] + public async Task Create_ShallReturnBadRequestOnValidationFailure() + { + var virtualAeTitle = new VirtualApplicationEntity + { + Name = "AeTitleIsTooooooLooooong", + VirtualAeTitle = "AeTitleIsTooooooLooooong", + Workflows = new List { "A", "B" } + }; + + var result = await _controller.Create(virtualAeTitle); + + Assert.NotNull(result); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Validation error.", problem.Title); + Assert.Equal("'AeTitleIsTooooooLooooong' is not a valid AE Title (source: virtualAeTitle).", problem.Detail); + Assert.Equal((int)HttpStatusCode.BadRequest, problem.Status); + } + + [RetryFact(5, 250, DisplayName = "Create - Shall return problem if failed to add")] + public async Task Create_ShallReturnBadRequestOnAddFailure() + { + var aeTitle = "AET"; + var virtualAeTitle = new VirtualApplicationEntity + { + Name = aeTitle, + VirtualAeTitle = aeTitle, + Workflows = new List { "A", "B" } + }; + + _repository.Setup(p => p.AddAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.Create(virtualAeTitle); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error adding new Virtual Application Entity.", problem.Title); + Assert.Equal($"error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + + _repository.Verify(p => p.AddAsync(It.IsAny(), It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Create - Shall return CreatedAtAction")] + public async Task Create_ShallReturnCreatedAtAction() + { + var aeTitle = "AET"; + var virtualAeTitle = new VirtualApplicationEntity + { + Name = aeTitle, + VirtualAeTitle = aeTitle, + Workflows = new List { "A", "B" } + }; + + _repository.Setup(p => p.AddAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Create(virtualAeTitle); + + Assert.IsType(result.Result); + + _repository.Verify(p => p.AddAsync(It.Is(p => p.CreatedBy == TestUsername), It.IsAny()), Times.Once()); + } + + #endregion Create + + #region Update + + [RetryFact(DisplayName = "Update - Shall return updated")] + public async Task Update_ReturnsUpdated() + { + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = "AET", + Name = "AET", + Workflows = new List { "1", "2", "3" }, + PlugInAssemblies = new List { "A", "B", "C" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as VirtualApplicationEntity; + + Assert.Equal(entity.VirtualAeTitle, updatedEntity.VirtualAeTitle); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.Equal(entity.Workflows, updatedEntity.Workflows); + Assert.Equal(entity.PlugInAssemblies, updatedEntity.PlugInAssemblies); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); + + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(entity, It.IsAny()), Times.Once()); + } + + [RetryFact(DisplayName = "Update - Shall return updated without updating AE Title")] + public async Task Update_ReturnsUpdated_WithoutUpdatingAET() + { + var originalEntity = new VirtualApplicationEntity + { + VirtualAeTitle = "AET", + Name = "AET", + Workflows = new List { "1", "2", "3" }, + }; + + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = "SHOUD-NOT-CHANGE", + Name = "AET", + Workflows = new List { "1", "2" }, + PlugInAssemblies = new List { "A", "B" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(originalEntity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Edit(entity); + var okResult = result.Result as OkObjectResult; + Assert.Equal(StatusCodes.Status200OK, okResult.StatusCode); + var updatedEntity = okResult.Value as VirtualApplicationEntity; + + Assert.NotEqual(entity.VirtualAeTitle, updatedEntity.VirtualAeTitle); + Assert.Equal(originalEntity.VirtualAeTitle, updatedEntity.VirtualAeTitle); + Assert.Equal(entity.Name, updatedEntity.Name); + Assert.Equal(entity.Workflows, updatedEntity.Workflows); + Assert.Equal(entity.PlugInAssemblies, updatedEntity.PlugInAssemblies); + Assert.Collection(entity.PlugInAssemblies, p => p.Equals("A", StringComparison.CurrentCultureIgnoreCase), p => p.Equals("B", StringComparison.CurrentCultureIgnoreCase)); + Assert.NotNull(updatedEntity.DateTimeUpdated); + Assert.Equal(TestUsername, updatedEntity.UpdatedBy); + + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + _repository.Verify(p => p.UpdateAsync(updatedEntity, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return 404 if input is null")] + public async Task Update_Returns404IfInputIsNull() + { + var result = await _controller.Edit(null); + + Assert.IsType(result.Result); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return 404 if not found")] + public async Task Update_Returns404IfNotFound() + { + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = "AET", + Name = "AET", + Workflows = new List { "1", "2", "3" }, + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(default(VirtualApplicationEntity))); + + var result = await _controller.Edit(entity); + + Assert.IsType(result.Result); + _repository.Verify(p => p.FindByNameAsync(entity.Name, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return problem on failure")] + public async Task Update_ShallReturnProblemOnFailure() + { + var value = "AET"; + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = value, + Name = "AET", + Workflows = new List { "1", "2", "3" }, + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.UpdateAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.Edit(entity); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error updating Virtual Application Entity.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Update - Shall return problem on validation failure")] + public async Task Update_ShallReturnBadRequestWithBadAeTitle() + { + var aeTitle = "TOOOOOOOOOOOOOOOOOOOOOOOLONG"; + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = aeTitle, + Name = "AET", + Workflows = new List { "1", "2", "3" }, + }; + + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + var result = await _controller.Edit(entity); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Validation error.", problem.Title); + Assert.Equal($"'{aeTitle}' is not a valid AE Title (source: virtualAeTitle).", problem.Detail); + Assert.Equal((int)HttpStatusCode.BadRequest, problem.Status); + } + + #endregion Update + + #region Delete + + [RetryFact(5, 250, DisplayName = "Delete - Shall return deleted object")] + public async Task Delete_ReturnsDeleted() + { + var value = "AET"; + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = value, + Name = value + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + + _repository.Setup(p => p.RemoveAsync(It.IsAny(), It.IsAny())); + + var result = await _controller.Delete(value); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as VirtualApplicationEntity; + Assert.NotNull(response); + Assert.Equal(value, response.VirtualAeTitle); + + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + _repository.Verify(p => p.RemoveAsync(entity, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Delete - Shall return 404 if not found")] + public async Task Delete_Returns404IfNotFound() + { + var value = "AET"; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(default(VirtualApplicationEntity))); + + var result = await _controller.Delete(value); + + Assert.IsType(result.Result); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "Delete - Shall return problem on failure")] + public async Task Delete_ShallReturnProblemOnFailure() + { + var value = "AET"; + var entity = new VirtualApplicationEntity + { + VirtualAeTitle = value, + Name = value + }; + _repository.Setup(p => p.FindByNameAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(entity)); + _repository.Setup(p => p.RemoveAsync(It.IsAny(), It.IsAny())).Throws(new Exception("error")); + + var result = await _controller.Delete(value); + + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error deleting Virtual Application Entity.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _repository.Verify(p => p.FindByNameAsync(value, It.IsAny()), Times.Once()); + } + + #endregion Delete + + #region GetPlugIns + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return registered plug-ins")] + public void GetPlugIns_ReturnsRegisteredPlugIns() + { + var input = new Dictionary() { { "A", "1" }, { "B", "3" }, { "C", "3" } }; + + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Returns(input); + + var result = _controller.GetPlugIns(); + var okObjectResult = result.Result as OkObjectResult; + var response = okObjectResult.Value as IDictionary; + Assert.NotNull(response); + Assert.Equal(input, response); + + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + [RetryFact(5, 250, DisplayName = "GetPlugIns - Shall return problem on failure")] + public void GetPlugIns_ShallReturnProblemOnFailure() + { + _pluginFactory.Setup(p => p.RegisteredPlugIns()).Throws(new Exception("error")); + + var result = _controller.GetPlugIns(); + var objectResult = result.Result as ObjectResult; + Assert.NotNull(objectResult); + var problem = objectResult.Value as ProblemDetails; + Assert.NotNull(problem); + Assert.Equal("Error reading data input plug-ins.", problem.Title); + Assert.Equal("error", problem.Detail); + Assert.Equal((int)HttpStatusCode.InternalServerError, problem.Status); + _pluginFactory.Verify(p => p.RegisteredPlugIns(), Times.Once()); + } + + #endregion GetPlugIns + } +} diff --git a/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityHandlerTest.cs b/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityHandlerTest.cs old mode 100644 new mode 100755 index 6edd2d50f..2225312a2 --- a/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityHandlerTest.cs +++ b/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityHandlerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,21 +16,25 @@ using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; +using System.IO.Abstractions.TestingHelpers; using System.Threading.Tasks; using FellowOakDicom; using FellowOakDicom.Network; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; using Monai.Deploy.InformaticsGateway.Services.Connectors; using Monai.Deploy.InformaticsGateway.Services.Scp; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; +using Monai.Deploy.Messaging.Events; using Moq; using xRetry; using Xunit; @@ -43,10 +47,12 @@ public class ApplicationEntityHandlerTest private readonly Mock _serviceScopeFactory; private readonly Mock _serviceScope; + private readonly Mock _inputDataPlugInEngine; private readonly Mock _payloadAssembler; private readonly Mock _uploadQueue; + private readonly Mock _extAppDetailsRepo = new Mock(); private readonly IOptions _options; - private readonly Mock _fileSystem; + private readonly IFileSystem _fileSystem; private readonly IServiceProvider _serviceProvider; public ApplicationEntityHandlerTest() @@ -54,33 +60,38 @@ public ApplicationEntityHandlerTest() _logger = new Mock>(); _serviceScopeFactory = new Mock(); _serviceScope = new Mock(); + _inputDataPlugInEngine = new Mock(); _payloadAssembler = new Mock(); _uploadQueue = new Mock(); _options = Options.Create(new InformaticsGatewayConfiguration()); - _fileSystem = new Mock(); + _fileSystem = new MockFileSystem(); var services = new ServiceCollection(); services.AddScoped(p => _payloadAssembler.Object); services.AddScoped(p => _uploadQueue.Object); - services.AddScoped(p => _fileSystem.Object); + services.AddScoped(p => _fileSystem); + services.AddScoped(p => _inputDataPlugInEngine.Object); + + _inputDataPlugInEngine.Setup(p => p.Configure(It.IsAny>())); + _serviceProvider = services.BuildServiceProvider(); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); - _fileSystem.Setup(p => p.Path.Combine(It.IsAny(), It.IsAny())).Returns((string path1, string path2) => System.IO.Path.Combine(path1, path2)); - _fileSystem.Setup(p => p.File.Create(It.IsAny())).Returns(FileStream.Null); _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + _options.Value.Storage.TemporaryDataStorage = TemporaryDataStorageLocation.Memory; } - [RetryFact(5, 250)] + [RetryFact(1, 250)] public void GivenAApplicationEntityHandler_WhenInitialized_ExpectParametersToBeValidated() { - Assert.Throws(() => new ApplicationEntityHandler(null, null, null)); - Assert.Throws(() => new ApplicationEntityHandler(_serviceScopeFactory.Object, null, null)); - Assert.Throws(() => new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, null)); + Assert.Throws(() => new ApplicationEntityHandler(null, null, null, null)); + Assert.Throws(() => new ApplicationEntityHandler(_serviceScopeFactory.Object, null, null, null)); + Assert.Throws(() => new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, null, null)); + Assert.Throws(() => new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, null)); - _ = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + _ = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); } [RetryFact(5, 250)] @@ -94,13 +105,18 @@ public async Task GivenAApplicationEntityHandler_WhenHandleInstanceAsyncIsCalled IgnoredSopClasses = new List { DicomUID.SecondaryCaptureImageStorage.UID } }; - var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); var request = GenerateRequest(); var dicomToolkit = new DicomToolkit(); var uids = dicomToolkit.GetStudySeriesSopInstanceUids(request.File); - await Assert.ThrowsAsync(async () => await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids)); + await Assert.ThrowsAsync(async + () => await handler.HandleInstanceAsync(request, + aet.AeTitle, + "CALLING", + Guid.NewGuid(), + uids, InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger)); } [RetryFact(5, 250)] @@ -114,17 +130,17 @@ public async Task GivenACStoreRequest_WhenTheSopClassIsInTheIgnoreList_ExpectIns IgnoredSopClasses = new List { DicomUID.SecondaryCaptureImageStorage.UID } }; - var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); handler.Configure(aet, Configuration.DicomJsonOptions.Complete, true); var request = GenerateRequest(); var dicomToolkit = new DicomToolkit(); var uids = dicomToolkit.GetStudySeriesSopInstanceUids(request.File); - await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids); + await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids, InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); } [RetryFact(5, 250)] @@ -138,17 +154,17 @@ public async Task GivenACStoreRequest_WhenTheSopClassIsNotInTheAllowedList_Expec AllowedSopClasses = new List { DicomUID.UltrasoundImageStorage.UID } }; - var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); handler.Configure(aet, Configuration.DicomJsonOptions.Complete, true); var request = GenerateRequest(); var dicomToolkit = new DicomToolkit(); var uids = dicomToolkit.GetStudySeriesSopInstanceUids(request.File); - await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids); + await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids, InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Never()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); } [RetryFact(5, 250)] @@ -158,20 +174,79 @@ public async Task GivenACStoreRequest_WhenHandleInstanceAsyncIsCalled_ExpectADic { AeTitle = "TESTAET", Name = "TESTAET", - Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() } + Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() }, + PlugInAssemblies = new List() { typeof(TestInputDataPlugInAddWorkflow).AssemblyQualifiedName } }; - var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); handler.Configure(aet, Configuration.DicomJsonOptions.Complete, true); var request = GenerateRequest(); var dicomToolkit = new DicomToolkit(); var uids = dicomToolkit.GetStudySeriesSopInstanceUids(request.File); + _inputDataPlugInEngine.Setup(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny())) + .Returns((DicomFile dicomFile, FileStorageMetadata fileMetadata) => Task.FromResult(new Tuple(dicomFile, fileMetadata))); - await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids); + await handler.HandleInstanceAsync(request, aet.AeTitle, "CALLING", Guid.NewGuid(), uids, InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); _uploadQueue.Verify(p => p.Queue(It.IsAny()), Times.Once()); - _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + _payloadAssembler.Verify(p => p.Queue(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.Configure(It.IsAny>()), Times.Once()); + _inputDataPlugInEngine.Verify(p => p.ExecutePlugInsAsync(It.IsAny(), It.IsAny()), Times.Once()); + } + + [RetryFact(5, 250)] + public void GivenAConfiguredAETitle_WhenConfiguringAgainWithDifferentAETitle_ExpectAnExceptionToBeThrown() + { + var aet = new MonaiApplicationEntity() + { + AeTitle = "TESTAET", + Name = "TESTAET", + Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() } + }; + + var newAet = new MonaiApplicationEntity() + { + AeTitle = "TESTAET", + Name = "TESTAET", + Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() } + }; + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options, _extAppDetailsRepo.Object); + handler.Configure(aet, Configuration.DicomJsonOptions.Complete, true); + + newAet.AeTitle = "NewAETitle"; + Assert.Throws(() => handler.Configure(newAet, Configuration.DicomJsonOptions.Complete, true)); + + newAet.AeTitle = "TESTAET"; + newAet.Name = "NewName"; + Assert.Throws(() => handler.Configure(newAet, Configuration.DicomJsonOptions.Complete, true)); + } + + [RetryFact(5, 250)] + public void GivenAConfiguredAETitle_WhenConfiguringAgainWithDifferentAETitle_ExpectAnExceptionToBeThrown() + { + var aet = new MonaiApplicationEntity() + { + AeTitle = "TESTAET", + Name = "TESTAET", + Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() } + }; + + var newAet = new MonaiApplicationEntity() + { + AeTitle = "TESTAET", + Name = "TESTAET", + Workflows = new List() { "AppA", "AppB", Guid.NewGuid().ToString() } + }; + var handler = new ApplicationEntityHandler(_serviceScopeFactory.Object, _logger.Object, _options); + handler.Configure(aet, Configuration.DicomJsonOptions.Complete, true); + + newAet.AeTitle = "NewAETitle"; + Assert.Throws(() => handler.Configure(newAet, Configuration.DicomJsonOptions.Complete, true)); + + newAet.AeTitle = "TESTAET"; + newAet.Name = "NewName"; + Assert.Throws(() => handler.Configure(newAet, Configuration.DicomJsonOptions.Complete, true)); } private static DicomCStoreRequest GenerateRequest() diff --git a/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityManagerTest.cs b/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityManagerTest.cs old mode 100644 new mode 100755 index c9e010a0a..8a7306729 --- a/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityManagerTest.cs +++ b/src/InformaticsGateway/Test/Services/Scp/ApplicationEntityManagerTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +26,11 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Database.Api.Repositories; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Scp; using Monai.Deploy.InformaticsGateway.Services.Storage; using Monai.Deploy.InformaticsGateway.SharedTest; @@ -108,7 +110,7 @@ public async Task HandleCStoreRequest_ShallThrowIfAENotConfigured() var request = GenerateRequest(); var exception = await Assert.ThrowsAsync(async () => { - await manager.HandleCStoreRequest(request, "BADAET", "CallingAET", Guid.NewGuid()); + await manager.HandleCStoreRequest(request, "BADAET", "CallingAET", Guid.NewGuid(), InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); }); Assert.Equal("Called AE Title 'BADAET' is not configured", exception.Message); @@ -138,7 +140,7 @@ public async Task HandleCStoreRequest_ThrowWhenOnLowStorageSpace() var request = GenerateRequest(); await Assert.ThrowsAsync(async () => { - await manager.HandleCStoreRequest(request, aet, "CallingAET", Guid.NewGuid()); + await manager.HandleCStoreRequest(request, aet, "CallingAET", Guid.NewGuid(), InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); }); _logger.VerifyLogging($"{aet} added to AE Title Manager.", LogLevel.Information, Times.Once()); @@ -170,8 +172,8 @@ public async Task IsValidSource_FalseWhenAEIsEmptyAsync() _monaiAeChangedNotificationService, _connfiguration); - Assert.False(await manager.IsValidSourceAsync(" ", "123").ConfigureAwait(false)); - Assert.False(await manager.IsValidSourceAsync("AAA", "").ConfigureAwait(false)); + Assert.False(await manager.IsValidSourceAsync(" ", "123").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + Assert.False(await manager.IsValidSourceAsync("AAA", "").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); } [RetryFact(5, 250, DisplayName = "IsValidSource - False when no matching source found")] @@ -193,7 +195,7 @@ public async Task IsValidSource_FalseWhenNoMatchingSourceAsync() )); var sourceAeTitle = "ValidSource"; - Assert.False(await manager.IsValidSourceAsync(sourceAeTitle, "1.2.3.4").ConfigureAwait(false)); + Assert.False(await manager.IsValidSourceAsync(sourceAeTitle, "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); _sourceEntityRepository.Verify(p => p.ContainsAsync(It.IsAny>>(), It.IsAny()), Times.Once()); _logger.VerifyLoggingMessageBeginsWith($"Available source AET: SAE @ 1.2.3.4.", LogLevel.Information, Times.Once()); @@ -211,7 +213,7 @@ public async Task IsValidSource_TrueAsync() _sourceEntityRepository.Setup(p => p.ContainsAsync(It.IsAny>>(), It.IsAny())) .ReturnsAsync(true); - Assert.True(await manager.IsValidSourceAsync(aet, "1.2.3.4").ConfigureAwait(false)); + Assert.True(await manager.IsValidSourceAsync(aet, "1.2.3.4").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); _sourceEntityRepository.Verify(p => p.ContainsAsync(It.IsAny>>(), It.IsAny()), Times.Once()); } @@ -231,7 +233,23 @@ public async Task ShallHandleAEChangeEventsAsync() AeTitle = "AE1", Name = "AE1" }, ChangedEventType.Added)); - Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(false)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE1", + Name = "AE1" + }, ChangedEventType.Updated)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE1", + Name = "AE1" + }, ChangedEventType.Updated)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( new MonaiApplicationEntity @@ -239,7 +257,63 @@ public async Task ShallHandleAEChangeEventsAsync() AeTitle = "AE1", Name = "AE1" }, ChangedEventType.Deleted)); - Assert.False(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(false)); + Assert.False(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + } + + [RetryFact(5, 250, DisplayName = "Shall prevent AE update when AE Title do not match")] + public async Task ShallPreventAEUpdateWHenAETDoNotMatchAsync() + { + _applicationEntityRepository.Setup(p => p.ToListAsync(It.IsAny())).ReturnsAsync(new List()); + var manager = new ApplicationEntityManager(_hostApplicationLifetime.Object, + _serviceScopeFactory.Object, + _monaiAeChangedNotificationService, + _connfiguration); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE1", + Name = "AE1" + }, ChangedEventType.Added)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE2", + Name = "AE1" + }, ChangedEventType.Updated)); + + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + Assert.False(await manager.IsAeTitleConfiguredAsync("AE2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + } + + [RetryFact(5, 250, DisplayName = "Shall prevent AE update when AE Title do not match")] + public async Task ShallPreventAEUpdateWHenAETDoNotMatchAsync() + { + _applicationEntityRepository.Setup(p => p.ToListAsync(It.IsAny())).ReturnsAsync(new List()); + var manager = new ApplicationEntityManager(_hostApplicationLifetime.Object, + _serviceScopeFactory.Object, + _monaiAeChangedNotificationService, + _connfiguration); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE1", + Name = "AE1" + }, ChangedEventType.Added)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + + _monaiAeChangedNotificationService.Notify(new MonaiApplicationentityChangedEvent( + new MonaiApplicationEntity + { + AeTitle = "AE2", + Name = "AE1" + }, ChangedEventType.Updated)); + + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); + Assert.False(await manager.IsAeTitleConfiguredAsync("AE2").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); } [RetryFact(5, 250, DisplayName = "Shall handle CS Store Request")] @@ -258,10 +332,10 @@ public async Task ShallHandleCStoreRequest() AeTitle = "AE1", Name = "AE1" }, ChangedEventType.Added)); - Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(false)); + Assert.True(await manager.IsAeTitleConfiguredAsync("AE1").ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext)); var request = GenerateRequest(); - await manager.HandleCStoreRequest(request, "AE1", "AE", associationId); + await manager.HandleCStoreRequest(request, "AE1", "AE", associationId, InformaticsGateway.Services.Common.ScpInputTypeEnum.WorkflowTrigger); _applicationEntityHandler.Verify(p => p.HandleInstanceAsync( @@ -273,7 +347,8 @@ public async Task ShallHandleCStoreRequest() p.SopClassUid.Equals(request.Dataset.GetSingleValue(DicomTag.SOPClassUID)) && p.StudyInstanceUid.Equals(request.Dataset.GetSingleValue(DicomTag.StudyInstanceUID)) && p.SeriesInstanceUid.Equals(request.Dataset.GetSingleValue(DicomTag.SeriesInstanceUID)) && - p.SopInstanceUid.Equals(request.Dataset.GetSingleValue(DicomTag.SOPInstanceUID)))) + p.SopInstanceUid.Equals(request.Dataset.GetSingleValue(DicomTag.SOPInstanceUID))), + ScpInputTypeEnum.WorkflowTrigger) , Times.Once()); } diff --git a/src/InformaticsGateway/Test/Services/Scp/MonaiAeChangedNotificationServiceTest.cs b/src/InformaticsGateway/Test/Services/Scp/MonaiAeChangedNotificationServiceTest.cs old mode 100644 new mode 100755 index 299a85afe..9dca2a3ca --- a/src/InformaticsGateway/Test/Services/Scp/MonaiAeChangedNotificationServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Scp/MonaiAeChangedNotificationServiceTest.cs @@ -16,7 +16,7 @@ using System; using Microsoft.Extensions.Logging; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Services.Scp; using Moq; using xRetry; diff --git a/src/InformaticsGateway/Test/Services/Scp/ScpServiceTest.cs b/src/InformaticsGateway/Test/Services/Scp/ScpServiceTest.cs old mode 100644 new mode 100755 index 1c3dcde04..42e4043f5 --- a/src/InformaticsGateway/Test/Services/Scp/ScpServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Scp/ScpServiceTest.cs @@ -28,6 +28,7 @@ using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Services.Common; using Monai.Deploy.InformaticsGateway.Services.Scp; using Monai.Deploy.InformaticsGateway.SharedTest; using Moq; @@ -44,7 +45,6 @@ public class ScpServiceTest private readonly Mock _associationDataProvider; private readonly Mock _loggerFactory; private readonly Mock> _logger; - private readonly Mock _loggerInternal; private readonly Mock _appLifetime; private readonly IOptions _configuration; private readonly CancellationTokenSource _cancellationTokenSource; @@ -56,7 +56,6 @@ public ScpServiceTest() _associationDataProvider = new Mock(); _loggerFactory = new Mock(); _logger = new Mock>(); - _loggerInternal = new Mock(); _appLifetime = new Mock(); _configuration = Options.Create(new InformaticsGatewayConfiguration()); _cancellationTokenSource = new CancellationTokenSource(); @@ -67,10 +66,8 @@ public ScpServiceTest() _serviceScope.Setup(x => x.ServiceProvider).Returns(serviceProvider.Object); _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); _loggerFactory.Setup(p => p.CreateLogger(It.IsAny())).Returns(_logger.Object); - _associationDataProvider.Setup(p => p.GetLogger(It.IsAny())).Returns(_loggerInternal.Object); _associationDataProvider.Setup(p => p.Configuration).Returns(_configuration); _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); - _loggerInternal.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); } [RetryFact(5, 250, DisplayName = "StartAsync - shall stop application if failed to start SCP listner")] @@ -87,13 +84,13 @@ public void StartAsync_ShallStopApplicationIfListnerFailedToStart() } [RetryFact(5, 250, DisplayName = "StopAsync - shall be able to stop SCP listener")] - public void StopAsync_ShallBeAbleToStopListener() + public async Task StopAsync_ShallBeAbleToStopListener() { - var service = CreateService(); + var service = await CreateService(); _appLifetime.Verify(p => p.StopApplication(), Times.Never()); - service.StopAsync(_cancellationTokenSource.Token); + await service.StopAsync(_cancellationTokenSource.Token); Assert.Equal(ServiceStatus.Stopped, service.Status); } @@ -107,7 +104,7 @@ public async Task CEcho_ShallRejectCEchoRequests() _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); await client.AddRequestAsync(new DicomCEchoRequest()); @@ -122,7 +119,7 @@ public async Task CEcho_ShallRejectCEchoRequests() Assert.Equal(DicomRejectSource.ServiceUser, exception.RejectSource); Assert.Equal(DicomRejectResult.Permanent, exception.RejectResult); - _loggerInternal.VerifyLogging($"Verification service is disabled: rejecting association.", LogLevel.Warning, Times.Once()); + _logger.VerifyLogging($"Verification service is disabled: rejecting association.", LogLevel.Warning, Times.Once()); Assert.True(countdownEvent.Wait(1000)); } @@ -137,7 +134,7 @@ public async Task CEcho_ShallRejecUnknownCallingAET() _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); await client.AddRequestAsync(new DicomCEchoRequest()); @@ -165,7 +162,7 @@ public async Task CEcho_ShallRejecUnknownCalledAET() _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(false); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); await client.AddRequestAsync(new DicomCEchoRequest()); @@ -193,7 +190,7 @@ public async Task CEcho_ShallAccept() _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); await client.AddRequestAsync(new DicomCEchoRequest()); @@ -214,7 +211,7 @@ public async Task CStore_ShallRejecOnLowStorageSpace() _associationDataProvider.Setup(p => p.CanStore).Returns(false); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); await client.AddRequestAsync(new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile())); @@ -238,10 +235,10 @@ public async Task CStore_OnCStoreRequest_InsufficientStorageAvailableException() _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.CanStore).Returns(true); - _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new InsufficientStorageAvailableException()); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new InsufficientStorageAvailableException()); var countdownEvent = new CountdownEvent(3); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); @@ -270,9 +267,9 @@ public async Task CStore_OnCStoreRequest_IoException() _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.CanStore).Returns(true); - _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new IOException { HResult = Constants.ERROR_HANDLE_DISK_FULL }); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new IOException { HResult = Constants.ERROR_HANDLE_DISK_FULL }); var countdownEvent = new CountdownEvent(3); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); @@ -301,10 +298,10 @@ public async Task CStore_OnCStoreRequest_Exception() _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.CanStore).Returns(true); - _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new Exception()); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Throws(new Exception()); var countdownEvent = new CountdownEvent(3); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); @@ -333,10 +330,10 @@ public async Task CStore_OnCStoreRequest_Success() _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.CanStore).Returns(true); - _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); var countdownEvent = new CountdownEvent(3); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); @@ -365,10 +362,10 @@ public async Task CStore_OnClientAbort() _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); _associationDataProvider.Setup(p => p.CanStore).Returns(true); - _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); var countdownEvent = new CountdownEvent(1); - var service = CreateService(); + var service = await CreateService(); var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.Port, false, "STORESCU", "STORESCP"); var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); @@ -381,10 +378,45 @@ public async Task CStore_OnClientAbort() await client.SendAsync(_cancellationTokenSource.Token, DicomClientCancellationMode.ImmediatelyAbortAssociation); Assert.True(countdownEvent.Wait(2000)); - _loggerInternal.VerifyLogging($"Aborted {DicomAbortSource.ServiceUser} with reason {DicomAbortReason.NotSpecified}.", LogLevel.Warning, Times.Once()); + _logger.VerifyLogging($"Aborted {DicomAbortSource.ServiceUser} with reason {DicomAbortReason.NotSpecified}.", LogLevel.Warning, Times.Once()); } - private ScpService CreateService() + [RetryFact(5, 250, DisplayName = "C-STORE - ExternalApp OnCStoreRequest - SendType")] + public async Task CStore_OnCStoreRequest_SendsType() + { + ScpInputTypeEnum savedType = ScpInputTypeEnum.WorkflowTrigger; + _associationDataProvider.Setup(p => p.IsValidSourceAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true); + _associationDataProvider.Setup(p => p.IsAeTitleConfiguredAsync(It.IsAny())).ReturnsAsync(true); + _associationDataProvider.Setup(p => p.CanStore).Returns(true); + _associationDataProvider.Setup(p => p.HandleCStoreRequest(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((DicomCStoreRequest request, string b, string c, Guid d, ScpInputTypeEnum type) => savedType = type); + + var countdownEvent = new CountdownEvent(3); + var service = await CreateExternalAppService(); + + var client = DicomClientFactory.Create("localhost", _configuration.Value.Dicom.Scp.ExternalAppPort, false, "STORESCU", "STORESCP"); + var request = new DicomCStoreRequest(InstanceGenerator.GenerateDicomFile()); + await client.AddRequestAsync(request); + client.AssociationAccepted += (sender, e) => + { + countdownEvent.Signal(); + }; + client.AssociationReleased += (sender, e) => + { + countdownEvent.Signal(); + }; + request.OnResponseReceived += (DicomCStoreRequest request, DicomCStoreResponse response) => + { + Assert.Equal(DicomStatus.Success, response.Status); + countdownEvent.Signal(); + }; + + await client.SendAsync(); + Assert.True(countdownEvent.Wait(2000)); + Assert.Equal(ScpInputTypeEnum.ExternalAppReturn, savedType); + } + + private async Task CreateService() { var tryCount = 0; ScpService service = null; @@ -395,7 +427,7 @@ private ScpService CreateService() if (service != null) { service.Dispose(); - Thread.Sleep(100); + await Task.Delay(100); } service = new ScpService(_serviceScopeFactory.Object, _associationDataProvider.Object, _appLifetime.Object, _configuration); _ = service.StartAsync(_cancellationTokenSource.Token); @@ -404,5 +436,26 @@ private ScpService CreateService() Assert.Equal(ServiceStatus.Running, service.Status); return service; } + + private async Task CreateExternalAppService() + { + var tryCount = 0; + ExternalAppScpService service = null; + + do + { + _configuration.Value.Dicom.Scp.ExternalAppPort = Interlocked.Increment(ref s_nextPort); + if (service != null) + { + service.Dispose(); + await Task.Delay(100); + } + service = new ExternalAppScpService(_serviceScopeFactory.Object, _associationDataProvider.Object, _appLifetime.Object, _configuration); + _ = service.StartAsync(_cancellationTokenSource.Token); + } while (service.Status != ServiceStatus.Running && tryCount++ < 5); + + Assert.Equal(ServiceStatus.Running, service.Status); + return service; + } } } diff --git a/src/InformaticsGateway/Test/Services/Scu/ScuServiceTest.cs b/src/InformaticsGateway/Test/Services/Scu/ScuServiceTest.cs index 0b04bfa28..465ef6fdb 100644 --- a/src/InformaticsGateway/Test/Services/Scu/ScuServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Scu/ScuServiceTest.cs @@ -70,7 +70,7 @@ public ScuServiceTest(DicomScpFixture dicomScp) _dicomScp.Start(_port); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public void GivenAScuService_WhenInitialized_ExpectParametersToBeValidated() { Assert.Throws(() => new ScuService(null, null, null)); @@ -79,7 +79,7 @@ public void GivenAScuService_WhenInitialized_ExpectParametersToBeValidated() _ = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public void GivenAScuService_WhenStartAsyncIsCalled_ExpectServiceStatusToBeSet() { var svc = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); @@ -88,7 +88,7 @@ public void GivenAScuService_WhenStartAsyncIsCalled_ExpectServiceStatusToBeSet() Assert.Equal(ServiceStatus.Running, svc.Status); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public async Task GivenAValidDicomEntity_WhenRequestToCEcho_ExpectToReturnSucess() { var svc = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); @@ -96,7 +96,7 @@ public async Task GivenAValidDicomEntity_WhenRequestToCEcho_ExpectToReturnSucess Assert.Equal(ServiceStatus.Running, svc.Status); - var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, DicomScpFixture.s_aETITLE); + var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, DicomScpFixture.s_aETITLE, CancellationToken.None); var response = await _scuQueue.Queue(request, _cancellationTokenSource.Token); @@ -105,7 +105,7 @@ public async Task GivenAValidDicomEntity_WhenRequestToCEcho_ExpectToReturnSucess Assert.Empty(response.Message); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public async Task GivenACEchoRequest_WhenRejected_ReturnStatusAssociationRejected() { var svc = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); @@ -113,7 +113,7 @@ public async Task GivenACEchoRequest_WhenRejected_ReturnStatusAssociationRejecte Assert.Equal(ServiceStatus.Running, svc.Status); - var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, "BADAET"); + var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, "BADAET", CancellationToken.None); var response = await _scuQueue.Queue(request, _cancellationTokenSource.Token); @@ -122,7 +122,7 @@ public async Task GivenACEchoRequest_WhenRejected_ReturnStatusAssociationRejecte Assert.StartsWith("Association rejected", response.Message); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public async Task GivenACEchoRequest_WhenAborted_ReturnStatusAssociationAborted() { var svc = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); @@ -130,7 +130,7 @@ public async Task GivenACEchoRequest_WhenAborted_ReturnStatusAssociationAborted( Assert.Equal(ServiceStatus.Running, svc.Status); - var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, "ABORT"); + var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "localhost", _port, "ABORT", CancellationToken.None); var response = await _scuQueue.Queue(request, _cancellationTokenSource.Token); @@ -139,7 +139,7 @@ public async Task GivenACEchoRequest_WhenAborted_ReturnStatusAssociationAborted( Assert.StartsWith("Association Abort", response.Message); } - [RetryFact(10,200)] + [RetryFact(10, 200)] public async Task GivenACEchoRequest_WhenRemoteServerIsUnreachable_ReturnStatusAssociationRejected() { var svc = new ScuService(_serviceScopeFactory.Object, _logger.Object, _options); @@ -147,7 +147,7 @@ public async Task GivenACEchoRequest_WhenRemoteServerIsUnreachable_ReturnStatusA Assert.Equal(ServiceStatus.Running, svc.Status); - var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "UNKNOWNHOST123456789", _port, DicomScpFixture.s_aETITLE); + var request = new ScuWorkRequest(Guid.NewGuid().ToString(), RequestType.CEcho, "UNKNOWNHOST123456789", _port, DicomScpFixture.s_aETITLE, CancellationToken.None); var response = await _scuQueue.Queue(request, _cancellationTokenSource.Token); diff --git a/src/InformaticsGateway/Test/Services/Storage/ObjectUploadServiceTest.cs b/src/InformaticsGateway/Test/Services/Storage/ObjectUploadServiceTest.cs old mode 100644 new mode 100755 index 985c13058..8f3321cfe --- a/src/InformaticsGateway/Test/Services/Storage/ObjectUploadServiceTest.cs +++ b/src/InformaticsGateway/Test/Services/Storage/ObjectUploadServiceTest.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Common; using Monai.Deploy.InformaticsGateway.Configuration; -using Monai.Deploy.InformaticsGateway.Repositories; using Monai.Deploy.InformaticsGateway.Services.Storage; +using Monai.Deploy.InformaticsGateway.SharedTest; using Monai.Deploy.Storage.API; using Moq; using xRetry; @@ -70,9 +70,9 @@ public ObjectUploadServiceTest() _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); _options.Value.Storage.TemporaryStorageBucket = "bucket"; + _options.Value.Storage.Retries.DelaysMilliseconds = new[] { 1 }; - _storageService.Setup(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny>())) - .Returns((string _, KeyValuePair input) => Task.FromResult(input)); + _storageService.Setup(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(true); } [RetryFact(10, 250)] @@ -97,7 +97,7 @@ public void GivenAObjectUploadService_WhenStartAsyncIsCalled_ExpectServiceStatus public void GivenAObjectUploadService_WhenInitialized_ExpectItToRemovingAllPendingObjects() { var svc = new ObjectUploadService(_serviceScopeFactory.Object, _logger.Object, _options); - + Assert.NotNull(svc); } [RetryFact(10, 250)] @@ -123,6 +123,34 @@ public async Task GivenADicomFileStorageMetadata_WhenQueuedForUpload_ExpectTwoFi } [RetryFact(10, 250)] + public async Task GivenADicomFileStorageMetadata_WhenVerificationFailsOver3Times_ExpectExceptionToBeThrow() + { + _options.Value.Storage.Retries.DelaysMilliseconds = new[] { 1 }; + var countdownEvent = new CountdownEvent(3); + _storageService.Setup(p => p.PutObjectAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())); + _storageService.Setup(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(() => + { + countdownEvent.Signal(); + }) + .ReturnsAsync(false); + + var svc = new ObjectUploadService(_serviceScopeFactory.Object, _logger.Object, _options); + _ = svc.StartAsync(_cancellationTokenSource.Token); + + Assert.Equal(ServiceStatus.Running, svc.Status); + + var file = await GenerateDicomFileStorageMetadata(); + _uploadQueue.Queue(file); + + Assert.True(countdownEvent.Wait(TimeSpan.FromSeconds(3))); + + _storageService.Verify(p => p.PutObjectAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once()); + _storageService.Verify(p => p.VerifyObjectExistsAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(3)); + _logger.VerifyLoggingMessageBeginsWith("Failed to upload file", LogLevel.Warning, Times.Once()); + } + + [RetryFact(10, 25000)] public async Task GivenAFhirFileStorageMetadata_WhenQueuedForUpload_ExpectSingleFileToBeUploaded() { var countdownEvent = new CountdownEvent(1); @@ -146,19 +174,16 @@ public async Task GivenAFhirFileStorageMetadata_WhenQueuedForUpload_ExpectSingle private async Task GenerateFhirFileStorageMetadata() { - var file = new FhirFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), FhirStorageFormat.Json); + var file = new FhirFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), FhirStorageFormat.Json, Messaging.Events.DataService.FHIR, "origin"); await file.SetDataStream("[]", TemporaryDataStorageLocation.Memory); + file.PayloadId = Guid.NewGuid().ToString(); return file; } private async Task GenerateDicomFileStorageMetadata() { - var file = new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString()) - { - Source = "SOURCE", - CalledAeTitle = "AET" - }; + var file = new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), Messaging.Events.DataService.DIMSE, "SOURCE", "AET"); var dataset = new DicomDataset { { DicomTag.PatientID, "PID" }, @@ -169,6 +194,7 @@ private async Task GenerateDicomFileStorageMetadata() }; var dicomFile = new DicomFile(dataset); await file.SetDataStreams(dicomFile, "[]", TemporaryDataStorageLocation.Memory); + file.PayloadId = Guid.NewGuid().ToString(); return file; } } diff --git a/src/InformaticsGateway/Test/Services/Storage/StorageInfoProviderTest.cs b/src/InformaticsGateway/Test/Services/Storage/StorageInfoProviderTest.cs index ce3204da8..a6eec4385 100644 --- a/src/InformaticsGateway/Test/Services/Storage/StorageInfoProviderTest.cs +++ b/src/InformaticsGateway/Test/Services/Storage/StorageInfoProviderTest.cs @@ -37,7 +37,7 @@ public StorageInfoProviderTest() _configuration = Options.Create(new InformaticsGatewayConfiguration()); _driveInfo = new Mock(); - _fileSystem.Setup(p => p.DriveInfo.FromDriveName(It.IsAny())) + _fileSystem.Setup(p => p.DriveInfo.New(It.IsAny())) .Returns(_driveInfo.Object); _fileSystem.Setup(p => p.Directory.CreateDirectory(It.IsAny())); _fileSystem.Setup(p => p.Path.GetFullPath(It.IsAny())).Returns((string path) => System.IO.Path.GetFullPath(path)); diff --git a/src/InformaticsGateway/Test/Shared/DicomScpFixture.cs b/src/InformaticsGateway/Test/Shared/DicomScpFixture.cs index 77924612e..28f4a7f8d 100644 --- a/src/InformaticsGateway/Test/Shared/DicomScpFixture.cs +++ b/src/InformaticsGateway/Test/Shared/DicomScpFixture.cs @@ -82,8 +82,8 @@ public void Dispose() public class CStoreScp : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider { - public CStoreScp(INetworkStream stream, Encoding fallbackEncoding, FellowOakDicom.Log.ILogger log, DicomServiceDependencies dicomServiceDependencies) - : base(stream, fallbackEncoding, log, dicomServiceDependencies) + public CStoreScp(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, DicomServiceDependencies dicomServiceDependencies) + : base(stream, fallbackEncoding, logger, dicomServiceDependencies) { } @@ -121,7 +121,7 @@ public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) // ignore } - public void OnConnectionClosed(Exception exception) + public void OnConnectionClosedAsync(Exception exception) { // ignore } @@ -141,5 +141,8 @@ public Task OnCEchoRequestAsync(DicomCEchoRequest request) { return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success)); } + + public void OnConnectionClosed(Exception exception) + { } } } diff --git a/src/InformaticsGateway/Test/appsettings.json b/src/InformaticsGateway/Test/appsettings.json old mode 100644 new mode 100755 index c48c5da51..c600ee131 --- a/src/InformaticsGateway/Test/appsettings.json +++ b/src/InformaticsGateway/Test/appsettings.json @@ -1,4 +1,7 @@ { + "MonaiDeployAuthentication": { + "bypassAuthentication": true + }, "ConnectionStrings": { "Type": "Sqlite", "InformaticsGatewayDatabase": "Data Source=migdev.db" @@ -7,6 +10,7 @@ "dicom": { "scp": { "port": 1104, + "externalAppPort": 1106, "logDimseDatasets": false, "rejectUnknownSources": true }, @@ -32,7 +36,6 @@ "password": "password", "virtualHost": "monaideploy", "exchange": "monaideploy", - "exportRequestQueue": "export_tasks" } }, "storage": { diff --git a/src/InformaticsGateway/Test/packages.lock.json b/src/InformaticsGateway/Test/packages.lock.json index 8b1cd89ca..720e4cda1 100644 --- a/src/InformaticsGateway/Test/packages.lock.json +++ b/src/InformaticsGateway/Test/packages.lock.json @@ -1,148 +1,106 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { "coverlet.collector": { "type": "Direct", - "requested": "[3.2.0, )", - "resolved": "3.2.0", - "contentHash": "xjY8xBigSeWIYs4I7DgUHqSNoGqnHi7Fv7/7RZD02rvZyG3hlsjnQKiVKVWKgr9kRKgmV+dEfu8KScvysiC0Wg==" - }, - "Microsoft.AspNetCore.Mvc.WebApiCompatShim": { - "type": "Direct", - "requested": "[2.2.0, )", - "resolved": "2.2.0", - "contentHash": "YKovpp46Fgah0N8H4RGb+7x9vdjj50mS3NON910pYJFQmn20Cd1mYVkTunjy/DrZpvwmJ8o5Es0VnONSYVXEAQ==", - "dependencies": { - "Microsoft.AspNet.WebApi.Client": "5.2.6", - "Microsoft.AspNetCore.Mvc.Core": "2.2.0", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0" - } - }, - "Microsoft.EntityFrameworkCore.InMemory": { - "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "2h5Pyy5e5EJhvMVl1UGPTLst1Q/+8rwEIvjsFwQDrsOmbsgzlbkvCJM2K89wvjA3UKAn5nTyRxCzKu9MMaJYkg==", - "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11" - } + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", - "dependencies": { - "Castle.Core": "5.1.0" - } - }, - "NPOI": { - "type": "Direct", - "requested": "[2.5.6, )", - "resolved": "2.5.6", - "contentHash": "xQfr09LZN3fr4rjSuV3li+WJUo2LiSg00IUtnomzrKO51zhhavyIgvbZ1f8c8zwHXvRbFc1JB4PNZpxqyxizWw==", - "dependencies": { - "Portable.BouncyCastle": "1.8.9", - "SharpZipLib": "1.3.3", - "System.Configuration.ConfigurationManager": "4.5.0", - "System.Drawing.Common": "4.5.0" - } - }, - "Swashbuckle.AspNetCore": { - "type": "Direct", - "requested": "[6.4.0, )", - "resolved": "6.4.0", - "contentHash": "eUBr4TW0up6oKDA5Xwkul289uqSMgY0xGN4pnbOIBqCcN9VKGGaPvHX3vWaG/hvocfGDP+MGzMA0bBBKz2fkmQ==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Microsoft.Extensions.ApiDescription.Server": "6.0.5", - "Swashbuckle.AspNetCore.Swagger": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerGen": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerUI": "6.4.0" + "Castle.Core": "5.1.1" } }, "System.IO.Abstractions.TestingHelpers": { "type": "Direct", - "requested": "[17.2.3, )", - "resolved": "17.2.3", - "contentHash": "tkXvQbsfOIfeoGso+WptCuouFLiWt3EU8s0D8poqIVz1BJOOszkPuFbFgP2HUTJ9bp5n1HH89eFHILo6Oz5XUw==", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", "dependencies": { - "System.IO.Abstractions": "17.2.3" + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" } }, "xRetry": { "type": "Direct", - "requested": "[1.8.0, )", - "resolved": "1.8.0", - "contentHash": "H8KXWHBjQASwD4y/7L2j7j4KLmg8z4+mCV4atrhZvJVnCkVSKLkWe1lfKGmaCYkKt2dJnC4yH+tJXGqthSkGGg==", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", "dependencies": { "xunit.core": "[2.4.0, 3.0.0)" } }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AspNetCore.HealthChecks.MongoDb": { + "type": "Transitive", + "resolved": "8.1.0", + "contentHash": "NuVDrj7UXBVniePh6JnuM8ryZRWdOIGOBes3owg2WQV/1NPntpWqX/DYaP6SBduHULUp8XRbwAui8qKQAW4SDA==", "dependencies": { - "JetBrains.Annotations": "2021.3.0" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "MongoDB.Driver": "2.28.0" } }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, - "Crc32.NET": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", - "dependencies": { - "NETStandard.Library": "2.0.0" - } + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, "DnsClient": { "type": "Transitive", @@ -154,337 +112,211 @@ }, "DotNext": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "5Xp6G9U0MhSmfgxKklUUsOFfSg2VqF+/rkd7WyoUs7HqbnVd32bRw2rWW5o+rieHLzUlW/sagctPiaZqmeTA+g==", + "resolved": "5.3.1", + "contentHash": "PIkzfT1wsvWGXb4Zigpfb7kUfNQdmlKi57yXYeMRE+06/JzpruwrBnQvwtKGw0qgn9lM4EIXyVfo4gnUARFDRw==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.IO.Hashing": "8.0.0" } }, "DotNext.Threading": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "G/AogSunqiZZ/0H4y3Qy/YNveIB+6azddStmFxbxLWkruXZ27gXyoRQ9kQ2gpDbq/+YfMINz9nmTY5ZtuCzuyw==", + "resolved": "5.5.0", + "contentHash": "djd6MPTZA3n2VcpViD0D03tkReudsCWpAefIhiRQCWEgTcW0uW1cZqlTh7qX15Jd5sKBE9qB13mJsrchpu3MKQ==", "dependencies": { - "DotNext": "4.7.4", - "System.Threading.Channels": "6.0.0" + "DotNext": "5.3.1", + "System.Threading.Channels": "8.0.0" } }, "fo-dicom": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, - "fo-dicom.NLog": { - "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "k35FD+C9IcpTLjCF5tvCkBGUxJ+YvzoBsgb2VAtGQv+aVTu+HyoCnNVqccc4lVE53fbVCwpR3gPiTAnm5fm+KQ==", - "dependencies": { - "NLog": "4.7.11", - "fo-dicom": "5.0.3" - } - }, "HL7-dotnetcore": { "type": "Transitive", - "resolved": "2.29.0", - "contentHash": "E0N/W72HsvIJj6XGyiUv9BHmyhvkNedpa23QN/Xwk47S965NYC9JSA1VVYWAAs4J6yOIhpM3lBOEWvhQBO31Lw==" + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, - "JetBrains.Annotations": { + "Humanizer.Core": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, - "Karambolo.Extensions.Logging.File": { + "Humanizer.Core": { "type": "Transitive", - "resolved": "3.3.1", - "contentHash": "wkPTc/UEuSAwbO3/Ee+oCdotxncmc/DKwjM533Z0BKvJm94NLOvU2i7pifgMd6uAUJ8jy69OcFZRu7hXKbMW6g==", - "dependencies": { - "Microsoft.Extensions.FileProviders.Physical": "3.0.0", - "Microsoft.Extensions.Logging.Configuration": "3.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "3.0.0", - "System.Threading.Channels": "4.7.1" - } + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, - "Microsoft.AspNet.WebApi.Client": { - "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - } - }, - "Microsoft.AspNetCore.Authentication.Abstractions": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "VloMLDJMf3n/9ic5lCBOa42IBYJgyB1JhzLsL68Zqg+2bEPWfGBj/xCJy/LrKTArN0coOcZp3wyVTZlx0y9pHQ==", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - } - }, - "Microsoft.AspNetCore.Authentication.Core": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "XlVJzJ5wPOYW+Y0J6Q/LVTEyfS4ssLXmt60T0SPP+D8abVhBTl+cgw2gDHlyKYIkcJg7btMVh383NDkMVqD/fg==", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0" - } - }, - "Microsoft.AspNetCore.Authorization": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "/L0W8H3jMYWyaeA9gBJqS/tSWBegP9aaTM0mjRhxTttBY9z4RVDRYJ2CwPAmAXIuPr3r1sOw+CS8jFVRGHRezQ==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - } - }, - "Microsoft.AspNetCore.Authorization.Policy": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "aJCo6niDRKuNg2uS2WMEmhJTooQUGARhV2ENQ2tO5443zVHUo19MSgrgGo9FIrfD+4yKPF8Q+FF33WkWfPbyKw==", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Authorization": "2.2.0" - } - }, - "Microsoft.AspNetCore.Hosting.Abstractions": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "ubycklv+ZY7Kutdwuy1W4upWcZ6VFR8WUXU7l7B2+mvbDBBPAcfpi+E+Y5GFe+Q157YfA3C49D2GCjAZc7Mobw==", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.2.0" - } - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions": { - "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "1PMijw8RMtuQF60SsD/JlKtVfvh4NORAhF4wjysdABhlhTrYmtgssqyncR0Stq5vqtjplZcj6kbT4LRTglt9IQ==", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" - } - }, - "Microsoft.AspNetCore.Http": { + "Microsoft.AspNetCore.Authentication.JwtBearer": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "YogBSMotWPAS/X5967pZ+yyWPQkThxhmzAwyCHCSSldzYBkW5W5d6oPfBaPqQOnSHYTpSOSOkpZoAce0vwb6+A==", + "resolved": "8.0.14", + "contentHash": "e19jmWJAQucbPYk3/fihJMDCYfv6CO+Qwp34pOehUSCbaHROw6FZ551SN1D0s9Btl0U/QHfuwFq6Z8Oa2ktE6g==", "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0" + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2" } }, - "Microsoft.AspNetCore.Http.Abstractions": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - } + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "Microsoft.AspNetCore.Http.Extensions": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "2DgZ9rWrJtuR7RYiew01nGRzuQBDaGHGmK56Rk54vsLLsCdzuFUPqbDTJCS1qJQWTbmbIQ9wGIOjpxA1t0l7/w==", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Buffers": "4.5.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.AspNetCore.Http.Features": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - } + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" }, - "Microsoft.AspNetCore.JsonPatch": { + "Microsoft.CodeAnalysis.Common": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "o9BB9hftnCsyJalz9IT0DUFxz8Xvgh3TOfGWolpuf19duxB4FySq7c25XDYBmBMS+sun5/PsEUAi58ra4iJAoA==", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", "dependencies": { - "Microsoft.CSharp": "4.5.0", - "Newtonsoft.Json": "11.0.2" + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" } }, - "Microsoft.AspNetCore.Mvc.Abstractions": { + "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "ET6uZpfVbGR1NjCuLaLy197cQ3qZUjzl7EG5SL4GfJH/c9KRE89MMBrQegqWsh0w1iRUB/zQaK0anAjxa/pz4g==", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", "dependencies": { - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0" + "Microsoft.CodeAnalysis.Common": "[4.5.0]" } }, - "Microsoft.AspNetCore.Mvc.Core": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "ALiY4a6BYsghw8PT5+VU593Kqp911U3w9f/dH9/ZoI3ezDsDAGiObqPu/HP1oXK80Ceu0XdQ3F0bx5AXBeuN/Q==", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.2.0", - "Microsoft.AspNetCore.Authorization.Policy": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.Mvc.Abstractions": "2.2.0", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Routing": "2.2.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.DependencyModel": "2.1.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "System.Diagnostics.DiagnosticSource": "4.5.0", - "System.Threading.Tasks.Extensions": "4.5.1" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" } }, - "Microsoft.AspNetCore.Mvc.Formatters.Json": { + "Microsoft.CodeAnalysis.Workspaces.Common": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "ScWwXrkAvw6PekWUFkIr5qa9NKn4uZGRvxtt3DvtUrBYW5Iu2y4SS/vx79JN0XDHNYgAJ81nVs+4M7UE1Y/O+g==", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", "dependencies": { - "Microsoft.AspNetCore.JsonPatch": "2.2.0", - "Microsoft.AspNetCore.Mvc.Core": "2.2.0" + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" } }, - "Microsoft.AspNetCore.ResponseCaching.Abstractions": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "CIHWEKrHzZfFp7t57UXsueiSA/raku56TgRYauV/W1+KAQq6vevz60zjEKaazt3BI76zwMz3B4jGWnCwd8kwQw==", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - } + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.AspNetCore.Routing": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "jAhDBy0wryOnMhhZTtT9z63gJbvCzFuLm8yC6pHzuVu9ZD1dzg0ltxIwT4cfwuNkIL/TixdKsm3vpVOpG8euWQ==", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - } + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, - "Microsoft.AspNetCore.Routing.Abstractions": { + "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "lRRaPN7jDlUCVCp9i0W+PB0trFaKB0bgMJD7hEJS9Uo4R9MXaMC8X2tJhPLmeVE3SGDdYI4QNKdVmhNvMJGgPQ==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0" + "SQLitePCLRaw.core": "2.1.6" } }, - "Microsoft.AspNetCore.WebUtilities": { + "Microsoft.EntityFrameworkCore": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", "dependencies": { - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" - }, - "Microsoft.CodeCoverage": { + "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, - "Microsoft.CSharp": { + "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" }, - "Microsoft.Data.Sqlite.Core": { + "Microsoft.EntityFrameworkCore.Design": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" } }, - "Microsoft.EntityFrameworkCore": { + "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, - "Microsoft.EntityFrameworkCore.Abstractions": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" - }, - "Microsoft.EntityFrameworkCore.Analyzers": { - "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" - }, - "Microsoft.EntityFrameworkCore.Relational": { + "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, - "Microsoft.EntityFrameworkCore.Sqlite": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" } }, - "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "Microsoft.EntityFrameworkCore.Tools": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "nWlJaFEvT7HpLkOusyReeQPeHsl5EUIBlI5Pr05SYhkWFQ7KGLmUNdv4j1aAjrO+x42Enqr7fu7r3MXLPQqImg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.EntityFrameworkCore.Design": "8.0.14" } }, "Microsoft.Extensions.ApiDescription.Server": { @@ -494,348 +326,264 @@ }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.CommandLine": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.UserSecrets": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "E6HxKQvrm0AeDagW6w+CsyVfXAO/pscrbX6mQ+XnThdwkeTxi0cnuXDTiTmd+WSmofSfpBKOS0VlvHUOxskdLQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "MQS7GE1ux7Lo1yOr59M7ZTEoFY3GJ9hHkxXQnQc8EPxkt5S7cX4qe6djSWH+mk9qQan+AjFZzdC1x5Af5IaseA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "tMjF1erFhHE+SnsiIyRIatVeKBgB9OfGsOvQe/+foE0xl4+JUQGbCA7gF1wqOksi9AxmGWzqtqjsMKJpCo5wYQ==", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11" + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" - }, - "Microsoft.Extensions.Hosting": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Logging.Debug": "6.0.0", - "Microsoft.Extensions.Logging.EventLog": "6.0.0", - "Microsoft.Extensions.Logging.EventSource": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Http": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, - "Microsoft.Extensions.Logging.Console": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.Debug": { + "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.EventLog": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.EventLog": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Extensions.Logging.EventSource": { + "Microsoft.IdentityModel.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "6.0.0" - } + "resolved": "7.1.2", + "contentHash": "33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==" }, - "Microsoft.Extensions.ObjectPool": { + "Microsoft.IdentityModel.JsonWebTokens": { "type": "Transitive", - "resolved": "2.2.0", - "contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==" + "resolved": "7.1.2", + "contentHash": "cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.1.2" + } }, - "Microsoft.Extensions.Options": { + "Microsoft.IdentityModel.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.1.2", + "contentHash": "YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Abstractions": "7.1.2" } }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { + "Microsoft.IdentityModel.Protocols": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "7.1.2", + "contentHash": "SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Logging": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "7.1.2", + "contentHash": "6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.IdentityModel.Protocols": "7.1.2", + "System.IdentityModel.Tokens.Jwt": "7.1.2" } }, - "Microsoft.Net.Http.Headers": { + "Microsoft.IdentityModel.Tokens": { "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", + "resolved": "7.1.2", + "contentHash": "oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==", "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" + "Microsoft.IdentityModel.Logging": "7.1.2" } }, "Microsoft.NETCore.Platforms": { @@ -843,39 +591,28 @@ "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.3", - "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" - }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "1.2.3", - "contentHash": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==" + "resolved": "1.6.23", + "contentHash": "tZ1I0KXnn98CWuV8cpI247A17jaY+ILS9vvF7yhI0uPPEqF4P1d7BWL5Uwtel10w9NucllHB3nTkfYTAcHAh8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -885,433 +622,326 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "Microsoft.Win32.SystemEvents": { + "Minio": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "LuI1oG+24TUj1ZRQQjM5Ew73BKnZE5NZ/7eAdh1o8ST5dPhUnJvIkiIn2re3MwnkRy6ELRnvEbBxHP8uALKhJw==", + "resolved": "6.0.2", + "contentHash": "4Od4uGANX5X0AL90WV0viBNzpE2+jDHro6CGvR4//MVj5SiTTwR5SUikXgd/2G2PtYyXw4b/IBpo7Kt5cCCndA==", "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0" + "CommunityToolkit.HighPerformance": "8.2.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "System.IO.Hashing": "8.0.0", + "System.Reactive": "6.0.0" } }, - "Minio": { + "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "4.0.6", - "contentHash": "c/7hO1I0/PB1hJr7HXuNxsTT59xi4ADPxHhGtv7zzTNZqDqq+Vgv+C8xSJ6rlIy4px7ibhMt6Kunq20ZBlLj4Q==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Crc32.NET": "1.2.0", - "Microsoft.CSharp": "4.7.0", - "Newtonsoft.Json": "13.0.1", - "System.Net.Http": "4.3.4", - "System.Net.Primitives": "4.3.1", - "System.Reactive.Linq": "5.0.0", - "System.ValueTuple": "4.4.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" } }, - "Monai.Deploy.Messaging": { + "Monai.Deploy.Messaging.RabbitMQ": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, - "Monai.Deploy.Messaging.RabbitMQ": { + "Monai.Deploy.Security": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "P7JlaepxiuUxY3GmKhKnhkDKaHID5KFEE//fIwUkhS39WMYLzq3K63URzM/8EIPmuZQnxl2n7EEZVxafY4FHQg==", + "resolved": "1.0.1", + "contentHash": "GrGj/addv+V5MgZuHBXV68M9PtyzrPlvHibyaHPQq602wtK4CYqPGR5P4VJgM2ZuiW89vEKbhd07sxTFkG9Muw==", "dependencies": { - "Monai.Deploy.Messaging": "0.1.16", - "Polly": "7.2.3", - "RabbitMQ.Client": "6.4.0", - "System.Collections.Concurrent": "4.3.0" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.14", + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Logging.Configuration": "8.0.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.MinIO": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "k6j9u4x0gcMml2b5wUufItGvA8YqDnw7utxjsG1wSF7lGsqs5R5ajr9m6Z9//fSzFQ7bn1iMexnDLDhLLmo+eQ==", + "resolved": "1.0.2", + "contentHash": "H73iUS64UwZXK8ZxNUc3DvokEcptpcoYgY+FMJGlotCffGhqrK8iJ18THR0gg9Io5UgIv24mdu1jbUn/Cz2rhA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Minio": "4.0.6", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.S3Policy": "0.2.10" + "Minio": "6.0.2", + "Monai.Deploy.Storage": "1.0.2", + "Monai.Deploy.Storage.S3Policy": "1.0.2" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" }, - "NETStandard.Library": { + "Mono.TextTemplating": { "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, "NLog": { "type": "Transitive", - "resolved": "5.0.5", - "contentHash": "NOTWSyUEljmjMq7OqZ1X9iu4bJ+rW/o6pt79Jq8j2Q7s8DyoMMCJwe0HoCKcNjhYRJ++b+E8erH6E6WwaCTshQ==" + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "NLog.Extensions.Logging": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "cQCKF2/iYjZUkn0d2o6VD1xkTUhIFHPYmZEm29KlTthLEzMht5aY80SwWlHZCKy0w19kaSq1jgLJSGrKsapUfg==", + "resolved": "5.4.0", + "contentHash": "5T19INfbzRywZpyBGoQChsB/R7eHW9hR4Ml9O22NRjJpfltGIJ+rsoCcjwr2/V/E6i3/eXLTQwRAeDMICjWpTA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "NLog": "5.0.5" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "NLog": "5.4.0" } }, "NLog.Web.AspNetCore": { "type": "Transitive", - "resolved": "5.1.5", - "contentHash": "a7Pe6KdwIxPxcFYy6M7wLseU++tx1bBf/ROVNlcZPLfp40DPLA0KGOk1K9kvbcwPYKKLMikdSwiOyTRZZFlaXg==", + "resolved": "5.4.0", + "contentHash": "Le6rbipSqeGVygbLGgRwZNMsbjJ+YnoAMJdZVhgjQimXwmYbDNkSswK6Mb32bKoC5aIg5mhw+hpAmgWdegs4Eg==", "dependencies": { - "NLog.Extensions.Logging": "5.1.0" + "NLog.Extensions.Logging": "5.4.0" } }, - "NuGet.Frameworks": { - "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" - }, "Polly": { "type": "Transitive", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } }, - "Portable.BouncyCastle": { + "Polly.Core": { "type": "Transitive", - "resolved": "1.8.9", - "contentHash": "wlJo8aFoeyl+W93iFXTK5ShzDYk5WBqoUPjTNEM0Xv9kn1H+4hmuCjF0/n8HLm9Nnp1aY6KNndWqQTNk+NGgRQ==" + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, "RabbitMQ.Client": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1znR1gGU+xYVSpO5z8nQolcUKA/yydnxQn7Ug9+RUXxTSLMm/eE58VKGwahPBjELXvDnX0k/kBrAitFLRjx9LA==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "System.Memory": "4.5.4", - "System.Threading.Channels": "4.7.1" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "SharpCompress": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Snappier": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" + } }, - "runtime.native.System": { + "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "System.Memory": "4.5.3" } }, - "runtime.native.System.Net.Http": { + "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "SQLitePCLRaw.core": "2.1.6" } }, - "runtime.native.System.Security.Cryptography.Apple": { + "Swashbuckle.AspNetCore": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "resolved": "8.0.0", + "contentHash": "K9FzGTxmwfD+7sVf/FTq/TZFHBTXcROgdcg7gLFwKwgvXwaqTtjGVdam27j0kYfgZZyWlOKr+abmtyd2nAd5eA==", "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + "Microsoft.Extensions.ApiDescription.Server": "6.0.5", + "Swashbuckle.AspNetCore.Swagger": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerGen": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerUI": "8.0.0" } }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.Swagger": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", + "resolved": "8.0.0", + "contentHash": "+8Y4pVTWbnzotIk6d6rcwsHGpCchPDqqrvYkyGlI3go+pFaKM+4eX30iCyI0hvr0RMtObJCFhK6aDtlQFbEF1g==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + "Microsoft.OpenApi": "1.6.23" } }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.SwaggerGen": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" + "resolved": "8.0.0", + "contentHash": "skCeIQ93yMcUm1PQby5qitFM6KLIlLMj4/i8JHy86x2OFzxTNaaas2kUg6rNV3JvucFvYCNyImg7NMtZHErSzQ==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "8.0.0" + } }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.SwaggerUI": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" + "resolved": "8.0.0", + "contentHash": "IMqmgclFiZL2QIfopOmWYofZzckrl+SdMt1h4mKC0jc94F+uzt3IHA3YFC0CGlwBqTTSnxHqNUKomNTeAhZbYA==" }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "System.Buffers": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "System.CodeDom": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" - }, - "SharpCompress": { - "type": "Transitive", - "resolved": "0.30.1", - "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" - }, - "SharpZipLib": { - "type": "Transitive", - "resolved": "1.3.3", - "contentHash": "N8+hwhsKZm25tDJfWpBSW7EGhH/R7EMuiX+KJ4C4u+fCWVc1lJ5zg1u3S1RPPVYgTqhx/C3hxrqUpi6RwK5+Tg==" - }, - "Snappier": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" - }, - "SQLitePCLRaw.bundle_e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", - "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" - } - }, - "SQLitePCLRaw.core": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", - "dependencies": { - "System.Memory": "4.5.3" - } - }, - "SQLitePCLRaw.lib.e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" - }, - "SQLitePCLRaw.provider.e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", - "dependencies": { - "SQLitePCLRaw.core": "2.0.6" - } - }, - "Swashbuckle.AspNetCore.Swagger": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "nl4SBgGM+cmthUcpwO/w1lUjevdDHAqRvfUoe4Xp/Uvuzt9mzGUwyFCqa3ODBAcZYBiFoKvrYwz0rabslJvSmQ==", - "dependencies": { - "Microsoft.OpenApi": "1.2.3" - } + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" }, - "Swashbuckle.AspNetCore.SwaggerGen": { + "System.Collections.Immutable": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "lXhcUBVqKrPFAQF7e/ZeDfb5PMgE8n5t6L5B6/BQSpiwxgHzmBcx8Msu42zLYFTvR5PIqE9Q9lZvSQAcwCxJjw==", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.4.0" + "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "Swashbuckle.AspNetCore.SwaggerUI": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1Hh3atb3pi8c+v7n4/3N80Jj8RvLOXgWxzix6w3OZhB7zBGRwsy7FWr4e3hwgPweSBpwfElqj4V4nkjYabH9nQ==" - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Collections": { + "System.Composition": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" } }, - "System.Collections.Concurrent": { + "System.Composition.AttributedModel": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Collections.Immutable": { + "System.Composition.Convention": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Configuration.ConfigurationManager": { + "System.Composition.Hosting": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "UIFvaFfuKhLr9u5tWMxmVoDPkFeD+Qv8gUuap4aZgVGYSYMdERck4OhLN/2gulAc0nYTEigWXSJNNWshrmxnng==", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", "dependencies": { - "System.Security.Cryptography.ProtectedData": "4.5.0", - "System.Security.Permissions": "4.5.0" + "System.Composition.Runtime": "6.0.0" } }, - "System.Diagnostics.Debug": { + "System.Composition.Runtime": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition.TypedParts": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, "System.Diagnostics.EventLog": { @@ -1319,278 +949,149 @@ "resolved": "6.0.0", "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" }, - "System.Diagnostics.Tracing": { + "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "resolved": "7.1.2", + "contentHash": "Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "Microsoft.IdentityModel.JsonWebTokens": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "System.Drawing.Common": { + "System.IO.Abstractions": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "AiJFxxVPdeITstiRS5aAu8+8Dpf5NawTMoapZ53Gfirml24p7HIfhjmCRxdXnmmf3IUA3AX3CcW7G73CjWxW/Q==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0", - "Microsoft.Win32.SystemEvents": "4.5.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Globalization": { + "System.IO.Hashing": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" }, - "System.Globalization.Calendars": { + "System.IO.Pipelines": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, - "System.Globalization.Extensions": { + "System.Linq.Expressions": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - } - }, - "System.IO": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "System.Threading": "4.3.0" } }, - "System.IO.Abstractions": { + "System.Memory": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, - "System.IO.FileSystem": { + "System.Net.Sockets": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", "Microsoft.NETCore.Targets": "1.1.0", "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", + "System.Net.Primitives": "4.3.0", "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", "System.Threading.Tasks": "4.3.0" } }, - "System.IO.FileSystem.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } - }, - "System.Linq": { + "System.ObjectModel": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Async": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" - }, - "System.Net.Http": { - "type": "Transitive", - "resolved": "4.3.4", - "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "OHzPhSme78BbmLe9UBxHM69ZYjClS5URuhce6Ta4ikiLgaUGiG/X84fZpI6zy7CsUH5R9cYzI2tv9dWPqdTkUg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "System.Runtime": "4.3.1", - "System.Runtime.Handles": "4.3.0" + "System.Threading": "4.3.0" } }, "System.Reactive": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" - }, - "System.Reactive.Linq": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "IB4/qlV4T1WhZvM11RVoFUSZXPow9VWVeQ1uDkSKgz6bAO+gCf65H/vjrYlwyXmojSSxvfHndF9qdH43P/IuAw==", - "dependencies": { - "System.Reactive": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } + "resolved": "6.0.0", + "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" }, - "System.Reflection": { + "System.Reflection.Emit": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Reflection.Metadata": { - "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" - }, - "System.Reflection.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Resources.ResourceManager": { + "System.Reflection.Emit.ILGeneration": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime": { - "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3" - } - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.Extensions": { + "System.Reflection.Emit.Lightweight": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime.Handles": { + "System.Reflection.Extensions": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", "System.Runtime": "4.3.0" } }, - "System.Runtime.InteropServices": { + "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" + "System.Collections.Immutable": "6.0.0" } }, - "System.Runtime.Numerics": { + "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -1601,241 +1102,52 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "wLBKzFnDCxP12VL9ANydSYhk59fC4cvOr9ypYQLPnAj48NQIhqnjdD2yhP8yEKyBJEjERWS9DisKL7rX5eU25Q==" - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "9gdyuARhUR7H+p5CjyUB/zPk7/Xut3wUSP8NJQB6iZr8L3XUXTMdoLeVAg9N4rqF8oIpE7MpdqHdDHQ7XgJe0g==", - "dependencies": { - "System.Security.AccessControl": "4.5.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, - "System.Threading": { + "System.Threading.Channels": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" }, - "System.Threading.Channels": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions.TestingHelpers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==" + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "xunit.abstractions": { "type": "Transitive", @@ -1844,173 +1156,172 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "monai.deploy.informaticsgateway": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "DotNext.Threading": "4.7.4", - "HL7-dotnetcore": "2.29.0", - "Karambolo.Extensions.Logging.File": "3.3.1", - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Hosting": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "1.0.0", - "Monai.Deploy.Messaging.RabbitMQ": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.MinIO": "0.2.10", - "NLog": "5.0.5", - "NLog.Web.AspNetCore": "5.1.5", - "Polly": "7.2.3", - "Swashbuckle.AspNetCore": "6.4.0", - "fo-dicom": "5.0.3", - "fo-dicom.NLog": "5.0.3" + "DotNext.Threading": "[5.5.0, )", + "HL7-dotnetcore": "[2.39.1, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution": "[1.0.0, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Security": "[1.0.1, )", + "Monai.Deploy.Storage.MinIO": "[1.0.2, )", + "NLog.Web.AspNetCore": "[5.4.0, )", + "Swashbuckle.AspNetCore": "[8.0.0, )" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.MongoDB": "1.0.0" + "AspNetCore.HealthChecks.MongoDb": "[8.1.0, )", + "Microsoft.EntityFrameworkCore.Tools": "[8.0.14, )", + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.MongoDB": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.database.mongodb": { "type": "Project", "dependencies": { - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "MongoDB.Driver": "2.18.0", - "MongoDB.Driver.Core": "2.18.0" + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.plugins.remoteappexecution": { + "type": "Project", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "NLog": "[5.4.0, )", + "Polly": "[8.5.2, )" + } + }, + "monai.deploy.informaticsgateway.test.plugins": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )" } } } diff --git a/src/InformaticsGateway/Test/xunit.runner.json b/src/InformaticsGateway/Test/xunit.runner.json new file mode 100755 index 000000000..ecc886497 --- /dev/null +++ b/src/InformaticsGateway/Test/xunit.runner.json @@ -0,0 +1,4 @@ +{ + $schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "diagnosticMessages": true +} diff --git a/src/InformaticsGateway/appsettings.Development.json b/src/InformaticsGateway/appsettings.Development.json old mode 100644 new mode 100755 index 5d84172ad..c37228d88 --- a/src/InformaticsGateway/appsettings.Development.json +++ b/src/InformaticsGateway/appsettings.Development.json @@ -1,35 +1,49 @@ { + "MonaiDeployAuthentication": { + "BypassAuthentication": true + }, "ConnectionStrings": { - "InformaticsGatewayDatabase": "Data Source=migdev.db" + "Type": "mongodb", + "InformaticsGatewayDatabase": "mongodb://root:rootpassword@localhost:27017", + "DatabaseOptions": { + "DatabaseName": "InformaticsGateway", + "retries": { + "delays": [ + "750", + "1201", + "2500" + ] + } + } }, "InformaticsGateway": { "dicom": { "scp": { - "port": 1104, + "port": 104, "rejectUnknownSources": false } }, "messaging": { "publisherSettings": { - "endpoint": "localhost", + "endpoint": "127.0.0.1", "username": "rabbitmq", "password": "rabbitmq", "virtualHost": "monaideploy", "exchange": "monaideploy" }, "subscriberSettings": { - "endpoint": "localhost", + "endpoint": "127.0.0.1", "username": "rabbitmq", "password": "rabbitmq", "virtualHost": "monaideploy", - "exchange": "monaideploy", - "exportRequestQueue": "export_tasks" + "exchange": "monaideploy" } }, "storage": { + "concurrentUploads": 5, "localTemporaryStoragePath": "./payloads", "settings": { - "endpoint": "localhost:9000", + "endpoint": "127.0.0.1:9000", "accessKey": "minioadmin", "accessToken": "minioadmin", "securedConnection": false diff --git a/src/InformaticsGateway/appsettings.Test.json b/src/InformaticsGateway/appsettings.Test.json old mode 100644 new mode 100755 index 2dc28055f..a2441e86c --- a/src/InformaticsGateway/appsettings.Test.json +++ b/src/InformaticsGateway/appsettings.Test.json @@ -6,6 +6,7 @@ "dicom": { "scp": { "port": 1104, + "externalAppPort": 1106, "rejectUnknownSources": false } }, @@ -23,7 +24,6 @@ "password": "rabbitmq", "virtualHost": "monaideploy", "exchange": "monaideploy", - "exportRequestQueue": "export_tasks" } }, "storage": { @@ -59,4 +59,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/appsettings.json b/src/InformaticsGateway/appsettings.json old mode 100644 new mode 100755 index d8125499a..33bbea769 --- a/src/InformaticsGateway/appsettings.json +++ b/src/InformaticsGateway/appsettings.json @@ -1,12 +1,79 @@ { + "MonaiDeployAuthentication": { + "bypassAuthentication": false, + "openId": { + "realm": "{realm}", + "realmKey": "{realm-secret-key}", + "clientId": "{client-id}", + "audiences": [ + "{audiences}" + ], + "roleClaimType": "{roles}", + "claimMappings": { + "userClaims": [ + { + "claimType": "user-role", + "claimValues": [ + "monai-deploy-user" + ], + "endpoints": [ + "Stow", + "Fhir", + "Inference" + ] + }, + { + "claimType": "user-role", + "claimValues": [ + "pacs-admin" + ], + "endpoints": [ + "MonaiAeTitle", + "SourceAeTitle", + "DestinationAeTitle" + ] + } + ], + "adminClaims": [ + { + "claimType": "user-role", + "claimValues": [ + "monai-role-admin" + ] + } + ] + } + } + }, "ConnectionStrings": { "Type": "Sqlite", - "InformaticsGatewayDatabase": "Data Source=/database/mig.db" + "InformaticsGatewayDatabase": "Data Source=/database/mig.db", + "DatabaseOptions": { + "DatabaseName": "InformaticsGateway", + "retries": { + "delays": [ + "750", + "1201", + "2500" + ] + } + } + }, + "plugins": { + "Pseudonymise": { + "ConnectionString": "test connect", + "DatabaseName": "InformaticsGateway", + "KMS": {} + }, + "remoteApp": { + "ReplaceTags": "AccessionNumber" + } }, "InformaticsGateway": { "dicom": { "scp": { "port": 104, + "externalAppPort": 105, "logDimseDatasets": false, "rejectUnknownSources": true }, @@ -32,10 +99,15 @@ "password": "password", "virtualHost": "monaideploy", "exchange": "monaideploy", - "exportRequestQueue": "export_tasks", "deadLetterExchange": "monaideploy-dead-letter", "deliveryLimit": 3, - "requeueDelay": 30 + "requeueDelay": 3, + "prefetchCount": "5" + }, + "topics": { + "externalAppRequest": "md.externalapp.request", + "exportHl7": "md.export.hl7", + "exportHl7Complete": "md.export.hl7complete" } }, "storage": { @@ -45,7 +117,7 @@ "storageRootPath": "/payloads", "temporaryBucketName": "monaideploy", "serviceAssemblyName": "Monai.Deploy.Storage.MinIO.MinIoStorageService, Monai.Deploy.Storage.MinIO", - "watermarkPercent": 75, + "watermarkPercent": 95, "reserveSpaceGB": 5, "settings": { "endpoint": "localhost:9000", @@ -63,6 +135,9 @@ "maximumNumberOfConnections": 10, "clientTimeout": 60000, "sendAck": true + }, + "dicomWeb": { + "plugins": [] } }, "Kestrel": { @@ -82,4 +157,4 @@ "InformaticsGatewayServerEndpoint": "http://localhost:5000", "DockerImagePrefix": "ghcr.io/project-monai/monai-deploy-informatics-gateway" } -} +} \ No newline at end of file diff --git a/src/InformaticsGateway/nlog.config b/src/InformaticsGateway/nlog.config old mode 100644 new mode 100755 index b2bc92dcf..8798fce06 --- a/src/InformaticsGateway/nlog.config +++ b/src/InformaticsGateway/nlog.config @@ -23,8 +23,8 @@ limitations under the License. internalLogFile="${basedir}/logs/internal-nlog.txt"> - - + + @@ -33,35 +33,34 @@ limitations under the License. - + - + - - + + - - - - - - + + + + + + - @@ -78,13 +77,12 @@ limitations under the License. - + - + - diff --git a/src/InformaticsGateway/packages.lock.json b/src/InformaticsGateway/packages.lock.json index d5a356c2d..731abbf02 100644 --- a/src/InformaticsGateway/packages.lock.json +++ b/src/InformaticsGateway/packages.lock.json @@ -1,271 +1,139 @@ { "version": 1, "dependencies": { - "net6.0": { - "Ardalis.GuardClauses": { - "type": "Direct", - "requested": "[4.0.1, )", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", - "dependencies": { - "JetBrains.Annotations": "2021.3.0" - } - }, + "net8.0": { "DotNext.Threading": { "type": "Direct", - "requested": "[4.7.4, )", - "resolved": "4.7.4", - "contentHash": "G/AogSunqiZZ/0H4y3Qy/YNveIB+6azddStmFxbxLWkruXZ27gXyoRQ9kQ2gpDbq/+YfMINz9nmTY5ZtuCzuyw==", - "dependencies": { - "DotNext": "4.7.4", - "System.Threading.Channels": "6.0.0" - } - }, - "fo-dicom": { - "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", - "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Channels": "6.0.0" - } - }, - "fo-dicom.NLog": { - "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "k35FD+C9IcpTLjCF5tvCkBGUxJ+YvzoBsgb2VAtGQv+aVTu+HyoCnNVqccc4lVE53fbVCwpR3gPiTAnm5fm+KQ==", + "requested": "[5.5.0, )", + "resolved": "5.5.0", + "contentHash": "djd6MPTZA3n2VcpViD0D03tkReudsCWpAefIhiRQCWEgTcW0uW1cZqlTh7qX15Jd5sKBE9qB13mJsrchpu3MKQ==", "dependencies": { - "NLog": "4.7.11", - "fo-dicom": "5.0.3" + "DotNext": "5.3.1", + "System.Threading.Channels": "8.0.0" } }, - "GitVersion.MsBuild": { - "type": "Direct", - "requested": "[5.11.1, )", - "resolved": "5.11.1", - "contentHash": "JlJB4dAc/MpLQvbF8OeyMKotDo5EcgU2pXmB+MlTe64B1Y0fc9GTMiAHiyUiHLnFRnOtrcSi1C3BsfRTmlD0sA==" - }, "HL7-dotnetcore": { "type": "Direct", - "requested": "[2.29.0, )", - "resolved": "2.29.0", - "contentHash": "E0N/W72HsvIJj6XGyiUv9BHmyhvkNedpa23QN/Xwk47S965NYC9JSA1VVYWAAs4J6yOIhpM3lBOEWvhQBO31Lw==" - }, - "Karambolo.Extensions.Logging.File": { - "type": "Direct", - "requested": "[3.3.1, )", - "resolved": "3.3.1", - "contentHash": "wkPTc/UEuSAwbO3/Ee+oCdotxncmc/DKwjM533Z0BKvJm94NLOvU2i7pifgMd6uAUJ8jy69OcFZRu7hXKbMW6g==", - "dependencies": { - "Microsoft.Extensions.FileProviders.Physical": "3.0.0", - "Microsoft.Extensions.Logging.Configuration": "3.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "3.0.0", - "System.Threading.Channels": "4.7.1" - } - }, - "Microsoft.EntityFrameworkCore": { - "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" - }, - "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { - "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "tMjF1erFhHE+SnsiIyRIatVeKBgB9OfGsOvQe/+foE0xl4+JUQGbCA7gF1wqOksi9AxmGWzqtqjsMKJpCo5wYQ==", - "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11" - } - }, - "Microsoft.Extensions.Hosting": { - "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Logging.Debug": "6.0.0", - "Microsoft.Extensions.Logging.EventLog": "6.0.0", - "Microsoft.Extensions.Logging.EventSource": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } - }, - "Microsoft.Extensions.Logging": { - "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" - } + "requested": "[2.39.1, )", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, - "Microsoft.Extensions.Logging.Console": { + "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Text.Json": "6.0.0" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" } }, - "Microsoft.Extensions.Options": { + "Microsoft.EntityFrameworkCore.Design": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" } }, "Monai.Deploy.Messaging.RabbitMQ": { "type": "Direct", - "requested": "[0.1.16, )", - "resolved": "0.1.16", - "contentHash": "P7JlaepxiuUxY3GmKhKnhkDKaHID5KFEE//fIwUkhS39WMYLzq3K63URzM/8EIPmuZQnxl2n7EEZVxafY4FHQg==", + "requested": "[2.0.4, )", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Monai.Deploy.Messaging": "0.1.16", - "Polly": "7.2.3", - "RabbitMQ.Client": "6.4.0", - "System.Collections.Concurrent": "4.3.0" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, - "Monai.Deploy.Storage": { + "Monai.Deploy.Security": { "type": "Direct", - "requested": "[0.2.10, )", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", - "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "requested": "[1.0.1, )", + "resolved": "1.0.1", + "contentHash": "GrGj/addv+V5MgZuHBXV68M9PtyzrPlvHibyaHPQq602wtK4CYqPGR5P4VJgM2ZuiW89vEKbhd07sxTFkG9Muw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.14", + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Logging.Configuration": "8.0.1" } }, "Monai.Deploy.Storage.MinIO": { "type": "Direct", - "requested": "[0.2.10, )", - "resolved": "0.2.10", - "contentHash": "k6j9u4x0gcMml2b5wUufItGvA8YqDnw7utxjsG1wSF7lGsqs5R5ajr9m6Z9//fSzFQ7bn1iMexnDLDhLLmo+eQ==", + "requested": "[1.0.2, )", + "resolved": "1.0.2", + "contentHash": "H73iUS64UwZXK8ZxNUc3DvokEcptpcoYgY+FMJGlotCffGhqrK8iJ18THR0gg9Io5UgIv24mdu1jbUn/Cz2rhA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Minio": "4.0.6", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.S3Policy": "0.2.10" + "Minio": "6.0.2", + "Monai.Deploy.Storage": "1.0.2", + "Monai.Deploy.Storage.S3Policy": "1.0.2" } }, - "NLog": { - "type": "Direct", - "requested": "[5.0.5, )", - "resolved": "5.0.5", - "contentHash": "NOTWSyUEljmjMq7OqZ1X9iu4bJ+rW/o6pt79Jq8j2Q7s8DyoMMCJwe0HoCKcNjhYRJ++b+E8erH6E6WwaCTshQ==" - }, "NLog.Web.AspNetCore": { "type": "Direct", - "requested": "[5.1.5, )", - "resolved": "5.1.5", - "contentHash": "a7Pe6KdwIxPxcFYy6M7wLseU++tx1bBf/ROVNlcZPLfp40DPLA0KGOk1K9kvbcwPYKKLMikdSwiOyTRZZFlaXg==", + "requested": "[5.4.0, )", + "resolved": "5.4.0", + "contentHash": "Le6rbipSqeGVygbLGgRwZNMsbjJ+YnoAMJdZVhgjQimXwmYbDNkSswK6Mb32bKoC5aIg5mhw+hpAmgWdegs4Eg==", "dependencies": { - "NLog.Extensions.Logging": "5.1.0" + "NLog.Extensions.Logging": "5.4.0" } }, - "Polly": { - "type": "Direct", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" - }, "Swashbuckle.AspNetCore": { "type": "Direct", - "requested": "[6.4.0, )", - "resolved": "6.4.0", - "contentHash": "eUBr4TW0up6oKDA5Xwkul289uqSMgY0xGN4pnbOIBqCcN9VKGGaPvHX3vWaG/hvocfGDP+MGzMA0bBBKz2fkmQ==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "K9FzGTxmwfD+7sVf/FTq/TZFHBTXcROgdcg7gLFwKwgvXwaqTtjGVdam27j0kYfgZZyWlOKr+abmtyd2nAd5eA==", "dependencies": { "Microsoft.Extensions.ApiDescription.Server": "6.0.5", - "Swashbuckle.AspNetCore.Swagger": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerGen": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerUI": "6.4.0" + "Swashbuckle.AspNetCore.Swagger": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerGen": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerUI": "8.0.0" } }, - "AWSSDK.Core": { + "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" }, - "AWSSDK.SecurityToken": { + "AspNetCore.HealthChecks.MongoDb": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "8.1.0", + "contentHash": "NuVDrj7UXBVniePh6JnuM8ryZRWdOIGOBes3owg2WQV/1NPntpWqX/DYaP6SBduHULUp8XRbwAui8qKQAW4SDA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "MongoDB.Driver": "2.28.0" } }, - "Crc32.NET": { + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" + }, + "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "NETStandard.Library": "2.0.0" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -276,85 +144,181 @@ }, "DotNext": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "5Xp6G9U0MhSmfgxKklUUsOFfSg2VqF+/rkd7WyoUs7HqbnVd32bRw2rWW5o+rieHLzUlW/sagctPiaZqmeTA+g==", + "resolved": "5.3.1", + "contentHash": "PIkzfT1wsvWGXb4Zigpfb7kUfNQdmlKi57yXYeMRE+06/JzpruwrBnQvwtKGw0qgn9lM4EIXyVfo4gnUARFDRw==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.IO.Hashing": "8.0.0" + } + }, + "fo-dicom": { + "type": "Transitive", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" } }, - "JetBrains.Annotations": { + "Humanizer.Core": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" }, "Macross.Json.Extensions": { "type": "Transitive", "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.AspNetCore.Authentication.JwtBearer": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", + "resolved": "8.0.14", + "contentHash": "e19jmWJAQucbPYk3/fihJMDCYfv6CO+Qwp34pOehUSCbaHROw6FZ551SN1D0s9Btl0U/QHfuwFq6Z8Oa2ktE6g==", "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2" } }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } }, - "Microsoft.CSharp": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.6", + "contentHash": "Ms5e5QuBAjVIuQsGumeLvkgMiOpnj6wxPvwBIoe1NfTkseWK4NZYztnhgDlpkCPkrUmJEXLv69kl349Ours30Q==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "nWlJaFEvT7HpLkOusyReeQPeHsl5EUIBlI5Pr05SYhkWFQ7KGLmUNdv4j1aAjrO+x42Enqr7fu7r3MXLPQqImg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.EntityFrameworkCore.Design": "8.0.14" } }, "Microsoft.Extensions.ApiDescription.Server": { @@ -364,266 +328,279 @@ }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, - "Microsoft.Extensions.Configuration.CommandLine": { + "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "Microsoft.Extensions.Configuration.Json": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, - "Microsoft.Extensions.Configuration.FileExtensions": { + "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Configuration.Json": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" - } + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" }, - "Microsoft.Extensions.Configuration.UserSecrets": { + "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, - "Microsoft.Extensions.DependencyInjection": { + "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, - "Microsoft.Extensions.DependencyModel": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" - } + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, - "Microsoft.Extensions.Diagnostics.HealthChecks": { + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "E6HxKQvrm0AeDagW6w+CsyVfXAO/pscrbX6mQ+XnThdwkeTxi0cnuXDTiTmd+WSmofSfpBKOS0VlvHUOxskdLQ==", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "MQS7GE1ux7Lo1yOr59M7ZTEoFY3GJ9hHkxXQnQc8EPxkt5S7cX4qe6djSWH+mk9qQan+AjFZzdC1x5Af5IaseA==" + "resolved": "8.0.6", + "contentHash": "GiHK4B5Xsf5YzL73kup2zJoJfDx5RJYBd0LZN6dx8XyOSoZnh7NNrMP+vTLxAJVp//JxHk0KIhBnW9YuFrSVig==" + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" + } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, - "Microsoft.Extensions.Http": { + "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, - "Microsoft.Extensions.Logging.Debug": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.EventLog": { + "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.EventLog": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.EventSource": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { + "Microsoft.IdentityModel.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "7.1.2", + "contentHash": "YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Abstractions": "7.1.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.IdentityModel.Protocols": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "7.1.2", + "contentHash": "SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.IdentityModel.Logging": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.1.2", + "System.IdentityModel.Tokens.Jwt": "7.1.2" } }, - "Microsoft.Net.Http.Headers": { + "Microsoft.IdentityModel.Tokens": { "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", + "resolved": "7.1.2", + "contentHash": "oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==", "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" + "Microsoft.IdentityModel.Logging": "7.1.2" } }, "Microsoft.NETCore.Platforms": { @@ -631,20 +608,10 @@ "resolved": "5.0.0", "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, - "Microsoft.NETCore.Targets": { - "type": "Transitive", - "resolved": "1.1.3", - "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" - }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "1.2.3", - "contentHash": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==" - }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" + "resolved": "1.6.23", + "contentHash": "tZ1I0KXnn98CWuV8cpI247A17jaY+ILS9vvF7yhI0uPPEqF4P1d7BWL5Uwtel10w9NucllHB3nTkfYTAcHAh8g==" }, "Microsoft.Win32.Registry": { "type": "Transitive", @@ -657,312 +624,216 @@ }, "Minio": { "type": "Transitive", - "resolved": "4.0.6", - "contentHash": "c/7hO1I0/PB1hJr7HXuNxsTT59xi4ADPxHhGtv7zzTNZqDqq+Vgv+C8xSJ6rlIy4px7ibhMt6Kunq20ZBlLj4Q==", + "resolved": "6.0.2", + "contentHash": "4Od4uGANX5X0AL90WV0viBNzpE2+jDHro6CGvR4//MVj5SiTTwR5SUikXgd/2G2PtYyXw4b/IBpo7Kt5cCCndA==", "dependencies": { - "Crc32.NET": "1.2.0", - "Microsoft.CSharp": "4.7.0", - "Newtonsoft.Json": "13.0.1", - "System.Net.Http": "4.3.4", - "System.Net.Primitives": "4.3.1", - "System.Reactive.Linq": "5.0.0", - "System.ValueTuple": "4.4.0" + "CommunityToolkit.HighPerformance": "8.2.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "System.IO.Hashing": "8.0.0", + "System.Reactive": "6.0.0" } }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Storage": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" }, - "NETStandard.Library": { + "Mono.TextTemplating": { "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, - "Newtonsoft.Json.Bson": { + "NLog": { "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "5T19INfbzRywZpyBGoQChsB/R7eHW9hR4Ml9O22NRjJpfltGIJ+rsoCcjwr2/V/E6i3/eXLTQwRAeDMICjWpTA==", "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "NLog": "5.4.0" } }, - "NLog.Extensions.Logging": { + "Polly": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "cQCKF2/iYjZUkn0d2o6VD1xkTUhIFHPYmZEm29KlTthLEzMht5aY80SwWlHZCKy0w19kaSq1jgLJSGrKsapUfg==", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "NLog": "5.0.5" + "Polly.Core": "8.5.2" } }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, "RabbitMQ.Client": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1znR1gGU+xYVSpO5z8nQolcUKA/yydnxQn7Ug9+RUXxTSLMm/eE58VKGwahPBjELXvDnX0k/kBrAitFLRjx9LA==", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "System.Memory": "4.5.4", - "System.Threading.Channels": "4.7.1" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "SharpCompress": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==" + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Snappier": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==" + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, - "runtime.native.System": { + "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, - "runtime.native.System.Net.Http": { + "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" + "System.Memory": "4.5.3" } }, - "runtime.native.System.Security.Cryptography.Apple": { + "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + "SQLitePCLRaw.core": "2.1.6" } }, - "runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.Swagger": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", + "resolved": "8.0.0", + "contentHash": "+8Y4pVTWbnzotIk6d6rcwsHGpCchPDqqrvYkyGlI3go+pFaKM+4eX30iCyI0hvr0RMtObJCFhK6aDtlQFbEF1g==", "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + "Microsoft.OpenApi": "1.6.23" } }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.SwaggerGen": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==" + "resolved": "8.0.0", + "contentHash": "skCeIQ93yMcUm1PQby5qitFM6KLIlLMj4/i8JHy86x2OFzxTNaaas2kUg6rNV3JvucFvYCNyImg7NMtZHErSzQ==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "8.0.0" + } }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "Swashbuckle.AspNetCore.SwaggerUI": { "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==" + "resolved": "8.0.0", + "contentHash": "IMqmgclFiZL2QIfopOmWYofZzckrl+SdMt1h4mKC0jc94F+uzt3IHA3YFC0CGlwBqTTSnxHqNUKomNTeAhZbYA==" }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.2", - "contentHash": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==" - }, - "SharpCompress": { - "type": "Transitive", - "resolved": "0.30.1", - "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" - }, - "Snappier": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" - }, - "SQLitePCLRaw.bundle_e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" - } - }, - "SQLitePCLRaw.core": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", - "dependencies": { - "System.Memory": "4.5.3" - } - }, - "SQLitePCLRaw.lib.e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" - }, - "SQLitePCLRaw.provider.e_sqlite3": { - "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", - "dependencies": { - "SQLitePCLRaw.core": "2.0.6" - } - }, - "Swashbuckle.AspNetCore.Swagger": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "nl4SBgGM+cmthUcpwO/w1lUjevdDHAqRvfUoe4Xp/Uvuzt9mzGUwyFCqa3ODBAcZYBiFoKvrYwz0rabslJvSmQ==", - "dependencies": { - "Microsoft.OpenApi": "1.2.3" - } - }, - "Swashbuckle.AspNetCore.SwaggerGen": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "lXhcUBVqKrPFAQF7e/ZeDfb5PMgE8n5t6L5B6/BQSpiwxgHzmBcx8Msu42zLYFTvR5PIqE9Q9lZvSQAcwCxJjw==", - "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.4.0" + "System.Runtime": "4.3.0" } }, - "Swashbuckle.AspNetCore.SwaggerUI": { - "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1Hh3atb3pi8c+v7n4/3N80Jj8RvLOXgWxzix6w3OZhB7zBGRwsy7FWr4e3hwgPweSBpwfElqj4V4nkjYabH9nQ==" - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Collections": { + "System.CodeDom": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Collections.Concurrent": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -972,242 +843,147 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" - }, - "System.Diagnostics.Debug": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Diagnostics.DiagnosticSource": { + "System.Composition": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" } }, - "System.Diagnostics.EventLog": { + "System.Composition.AttributedModel": { "type": "Transitive", "resolved": "6.0.0", - "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" }, - "System.Diagnostics.Tracing": { + "System.Composition.Convention": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.AttributedModel": "6.0.0" } }, - "System.Globalization": { + "System.Composition.Hosting": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Composition.Runtime": "6.0.0" } }, - "System.Globalization.Calendars": { + "System.Composition.Runtime": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" }, - "System.Globalization.Extensions": { + "System.Composition.TypedParts": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" } }, - "System.IO": { + "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "resolved": "7.1.2", + "contentHash": "Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "Microsoft.IdentityModel.JsonWebTokens": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" - }, - "System.IO.FileSystem": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" } }, - "System.IO.FileSystem.Primitives": { + "System.IO.Hashing": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "dependencies": { - "System.Runtime": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" }, - "System.Linq": { + "System.IO.Pipelines": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, - "System.Linq.Async": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "0YhHcaroWpQ9UCot3Pizah7ryAzQhNvobLMSxeDIGmnXfkQn8u5owvpOH0K6EVB+z9L7u6Cc4W17Br/+jyttEQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" }, - "System.Net.Http": { + "System.Linq.Expressions": { "type": "Transitive", - "resolved": "4.3.4", - "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", "System.Collections": "4.3.0", "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", "System.Resources.ResourceManager": "4.3.0", "System.Runtime": "4.3.0", "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - } - }, - "System.Net.Primitives": { - "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "OHzPhSme78BbmLe9UBxHM69ZYjClS5URuhce6Ta4ikiLgaUGiG/X84fZpI6zy7CsUH5R9cYzI2tv9dWPqdTkUg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "System.Runtime": "4.3.1", - "System.Runtime.Handles": "4.3.0" + "System.Threading": "4.3.0" } }, - "System.Reactive": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" - }, - "System.Reactive.Linq": { + "System.Memory": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "IB4/qlV4T1WhZvM11RVoFUSZXPow9VWVeQ1uDkSKgz6bAO+gCf65H/vjrYlwyXmojSSxvfHndF9qdH43P/IuAw==", - "dependencies": { - "System.Reactive": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, - "System.Reflection": { + "System.Net.Sockets": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", "Microsoft.NETCore.Targets": "1.1.0", "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" } }, - "System.Reflection.Primitives": { + "System.ObjectModel": { "type": "Transitive", "resolved": "4.3.0", - "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, - "System.Resources.ResourceManager": { + "System.Reactive": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - } + "resolved": "6.0.0", + "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" }, - "System.Runtime": { + "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3" + "System.Collections.Immutable": "6.0.0" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -1215,50 +991,6 @@ "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, - "System.Runtime.Extensions": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.Handles": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, - "System.Runtime.InteropServices": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - } - }, - "System.Runtime.Numerics": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - } - }, "System.Security.AccessControl": { "type": "Transitive", "resolved": "5.0.0", @@ -1268,327 +1000,150 @@ "System.Security.Principal.Windows": "5.0.0" } }, - "System.Security.Cryptography.Algorithms": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Cng": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - } - }, - "System.Security.Cryptography.Csp": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - } - }, - "System.Security.Cryptography.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.OpenSsl": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, - "System.Security.Cryptography.Primitives": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } - }, - "System.Security.Cryptography.X509Certificates": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - } - }, "System.Security.Principal.Windows": { "type": "Transitive", "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - } - }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" - } + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" }, - "System.Threading": { + "System.Threading.Channels": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - } + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" }, - "System.Threading.Channels": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" + "TestableIO.System.IO.Abstractions": "21.3.1" } }, - "System.Threading.Tasks.Dataflow": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==" - }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.MongoDB": "1.0.0" + "AspNetCore.HealthChecks.MongoDb": "[8.1.0, )", + "Microsoft.EntityFrameworkCore.Tools": "[8.0.14, )", + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.MongoDB": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.database.mongodb": { "type": "Project", "dependencies": { - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "MongoDB.Driver": "2.18.0", - "MongoDB.Driver.Core": "2.18.0" + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.plugins.remoteappexecution": { + "type": "Project", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "NLog": "[5.4.0, )", + "Polly": "[8.5.2, )" } } } diff --git a/src/Monai.Deploy.InformaticsGateway.sln b/src/Monai.Deploy.InformaticsGateway.sln index 51c87fe4d..d851e6255 100644 --- a/src/Monai.Deploy.InformaticsGateway.sln +++ b/src/Monai.Deploy.InformaticsGateway.sln @@ -52,7 +52,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Database", "Database", "{29 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test", "Database\EntityFramework\Test\Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test.csproj", "{EA930DE2-33C4-447C-9E26-31387652D408}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Monai.Deploy.InformaticsGateway.Database.MongoDB", "Database\MongoDB\Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj", "{5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.Database.MongoDB", "Database\MongoDB\Monai.Deploy.InformaticsGateway.Database.MongoDB.csproj", "{5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test", "Database\MongoDB\Integration.Test\Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test.csproj", "{2F849556-44B6-484A-B612-CB0FA5D29AC6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution", "Plug-ins\RemoteAppExecution\Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.csproj", "{8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test", "Plug-ins\RemoteAppExecution\Test\Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.csproj", "{8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Monai.Deploy.InformaticsGateway.Test.PlugIns", "InformaticsGateway\Test\Plug-ins\Monai.Deploy.InformaticsGateway.Test.PlugIns.csproj", "{6C83469B-4B8A-416E-ACA7-09454D721352}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -352,6 +360,54 @@ Global {5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31}.Release|x64.Build.0 = Release|Any CPU {5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31}.Release|x86.ActiveCfg = Release|Any CPU {5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31}.Release|x86.Build.0 = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|x64.Build.0 = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Debug|x86.Build.0 = Debug|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|Any CPU.Build.0 = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|x64.ActiveCfg = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|x64.Build.0 = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|x86.ActiveCfg = Release|Any CPU + {2F849556-44B6-484A-B612-CB0FA5D29AC6}.Release|x86.Build.0 = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|x64.ActiveCfg = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|x64.Build.0 = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|x86.ActiveCfg = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Debug|x86.Build.0 = Debug|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|Any CPU.Build.0 = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|x64.ActiveCfg = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|x64.Build.0 = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|x86.ActiveCfg = Release|Any CPU + {8D82BD38-A21D-4C16-9FB0-616B9E7BFF23}.Release|x86.Build.0 = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|x64.ActiveCfg = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|x64.Build.0 = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|x86.ActiveCfg = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Debug|x86.Build.0 = Debug|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|Any CPU.Build.0 = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|x64.ActiveCfg = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|x64.Build.0 = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|x86.ActiveCfg = Release|Any CPU + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177}.Release|x86.Build.0 = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|x64.ActiveCfg = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|x64.Build.0 = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|x86.ActiveCfg = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Debug|x86.Build.0 = Debug|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|Any CPU.Build.0 = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|x64.ActiveCfg = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|x64.Build.0 = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|x86.ActiveCfg = Release|Any CPU + {6C83469B-4B8A-416E-ACA7-09454D721352}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -372,6 +428,9 @@ Global {7F56994D-5310-467F-96FC-87C26F151737} = {B8E99EF7-84EA-4D11-B722-9EE81B89CD86} {EA930DE2-33C4-447C-9E26-31387652D408} = {B8E99EF7-84EA-4D11-B722-9EE81B89CD86} {5ED73EEA-4DFA-426D-82E8-AA24D3CB4C31} = {290E4C9B-841D-4E2C-91A0-5A69BAB122F3} + {2F849556-44B6-484A-B612-CB0FA5D29AC6} = {B8E99EF7-84EA-4D11-B722-9EE81B89CD86} + {8EDD99D5-9FB5-40A3-BE47-B083D6DBC177} = {B8E99EF7-84EA-4D11-B722-9EE81B89CD86} + {6C83469B-4B8A-416E-ACA7-09454D721352} = {B8E99EF7-84EA-4D11-B722-9EE81B89CD86} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E23DC856-D033-49F6-9BC6-9F1D0ECD05CB} diff --git a/src/Plug-ins/RemoteAppExecution/Database/DatabaseRegistrar.cs b/src/Plug-ins/RemoteAppExecution/Database/DatabaseRegistrar.cs new file mode 100755 index 000000000..49f71b4b4 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/DatabaseRegistrar.cs @@ -0,0 +1,65 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database +{ + public class DatabaseRegistrar : DatabaseRegistrationBase + { + public override IServiceCollection Configure( + IServiceCollection services, + DatabaseType databaseType, + IConfigurationSection? connectionstringConfigurationSection, + IConfigurationSection? pluginsConfigurationSection, + ILoggerFactory loggerFactory) + { + Guard.Against.Null(services, nameof(services)); + Guard.Against.Null(connectionstringConfigurationSection, nameof(connectionstringConfigurationSection)); + + var logger = loggerFactory.CreateLogger(); + + switch (databaseType) + { + case DatabaseType.EntityFramework: + + services.AddDbContext(options => options.UseSqlite(connectionstringConfigurationSection[SR.DatabaseConnectionStringKey]), ServiceLifetime.Transient); + services.AddScoped(); + logger.AddedDbScope("IDatabaseMigrationManagerForPlugIns", "EntityFramework"); + services.AddScoped(); + logger.AddedDbScope("IRemoteAppExecutionRepository", "EntityFramework"); + break; + + case DatabaseType.MongoDb: + Guard.Against.Null(pluginsConfigurationSection, nameof(pluginsConfigurationSection)); + services.Configure(connectionstringConfigurationSection.GetSection("DatabaseOptions")); + services.AddScoped(); + logger.AddedDbScope("IDatabaseMigrationManagerForPlugIns", "MongoDb"); + services.AddScoped(); + logger.AddedDbScope("IRemoteAppExecutionRepository", "MongoDb"); + break; + } + + return services; + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/MigrationManager.cs b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/MigrationManager.cs new file mode 100755 index 000000000..b5f617a8a --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/MigrationManager.cs @@ -0,0 +1,48 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework +{ + public class MigrationManager : IDatabaseMigrationManagerForPlugIns + { + public IHost Migrate(IHost host) + { + using (var scope = host.Services.CreateScope()) + { + using (var dbContext = scope.ServiceProvider.GetRequiredService()) + { + try + { + dbContext.Database.Migrate(); + } + catch (Exception ex) + { + var logger = scope.ServiceProvider.GetService(); + logger?.Log(LogLevel.Critical, message: "Failed to migrate database", exception: ex); + throw; + } + } + } + return host; + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionConfiguration.cs b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionConfiguration.cs new file mode 100644 index 000000000..63973bb87 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionConfiguration.cs @@ -0,0 +1,65 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework +{ +#pragma warning disable CS8604, CS8603 + + internal class RemoteAppExecutionConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + var dictValueComparer = new ValueComparer>( + (c1, c2) => c1!.Equals(c2), + c => c.GetHashCode(), + c => c.ToDictionary(entry => entry.Key, entry => entry.Value)); + + var jsonSerializerSettings = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + builder.HasKey(j => j.Id); + + builder.Property(j => j.WorkflowInstanceId).IsRequired(); + builder.Property(j => j.ExportTaskId).IsRequired(); + builder.Property(j => j.CorrelationId).IsRequired(); + builder.Property(j => j.RequestTime).IsRequired(); + + builder.Property(j => j.StudyInstanceUid).IsRequired(); + builder.Property(j => j.SeriesInstanceUid).IsRequired(); + builder.Property(j => j.SopInstanceUid).IsRequired(); + + builder.Property(j => j.OriginalValues) + .HasConversion( + v => JsonSerializer.Serialize(v, jsonSerializerSettings), + v => JsonSerializer.Deserialize>(v, jsonSerializerSettings)) + .Metadata.SetValueComparer(dictValueComparer); + + builder.HasIndex(p => new { p.WorkflowInstanceId, p.ExportTaskId, p.StudyInstanceUid, p.SeriesInstanceUid }, "idx_remoteapp_all"); + builder.HasIndex(p => new { p.WorkflowInstanceId, p.ExportTaskId, p.StudyInstanceUid }, "idx_remoteapp_study"); + builder.HasIndex(p => p.SopInstanceUid, "idx_remoteapp_instance"); + } + } + +#pragma warning restore CS8604, CS8603 +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContext.cs b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContext.cs new file mode 100644 index 000000000..4e1755edf --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContext.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework +{ +#pragma warning disable CS8618 // Unread "private" fields should be removed + + public class RemoteAppExecutionDbContext : DbContext + { + public RemoteAppExecutionDbContext(DbContextOptions options) : base(options) + { + } + + public virtual DbSet RemoteAppExecutions { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ApplyConfiguration(new RemoteAppExecutionConfiguration()); + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.ConfigureWarnings(c => c.Log( + (CoreEventId.SaveChangesCompleted, LogLevel.Trace), + (CoreEventId.SaveChangesStarting, LogLevel.Trace), + (CoreEventId.DetectChangesStarting, LogLevel.Trace), + (CoreEventId.DetectChangesCompleted, LogLevel.Trace), + (CoreEventId.StartedTracking, LogLevel.Trace), + (CoreEventId.ContextInitialized, LogLevel.Trace), + (CoreEventId.StateChanged, LogLevel.Trace), + (CoreEventId.QueryCompilationStarting, LogLevel.Trace), + (CoreEventId.QueryExecutionPlanned, LogLevel.Trace), + (RelationalEventId.CommandExecuting, LogLevel.Trace), + (RelationalEventId.CommandExecuted, LogLevel.Trace), + (RelationalEventId.ConnectionClosing, LogLevel.Trace), + (RelationalEventId.ConnectionClosed, LogLevel.Trace), + (RelationalEventId.DataReaderDisposing, LogLevel.Trace), + (RelationalEventId.ConnectionOpening, LogLevel.Trace), + (RelationalEventId.ConnectionOpened, LogLevel.Trace), + (RelationalEventId.CommandCreating, LogLevel.Trace), + (RelationalEventId.CommandCreating, LogLevel.Trace), + (RelationalEventId.TransactionStarted, LogLevel.Trace), + (RelationalEventId.TransactionStarting, LogLevel.Trace), + (RelationalEventId.TransactionCommitted, LogLevel.Trace), + (RelationalEventId.TransactionCommitting, LogLevel.Trace), + (RelationalEventId.TransactionDisposed, LogLevel.Trace), + (RelationalEventId.CommandCreated, LogLevel.Trace) + )); + } + +#pragma warning restore CS8618 // Unread "private" fields should be removed +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContextFactory.cs b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContextFactory.cs new file mode 100644 index 000000000..897323d3f --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionDbContextFactory.cs @@ -0,0 +1,44 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework +{ + /// + /// Used to EF migration. + /// + public class RemoteAppExecutionDbContextFactory : IDesignTimeDbContextFactory + { + public RemoteAppExecutionDbContext CreateDbContext(string[] args) + { + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .Build(); + + var builder = new DbContextOptionsBuilder(); + + var connectionString = configuration.GetConnectionString(InformaticsGateway.Database.Api.SR.DatabaseConnectionStringKey); + builder.UseSqlite(connectionString); + + return new RemoteAppExecutionDbContext(builder.Options); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionRepository.cs b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionRepository.cs new file mode 100755 index 000000000..786080a79 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/EntityFramework/RemoteAppExecutionRepository.cs @@ -0,0 +1,139 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework +{ + public class RemoteAppExecutionRepository : IRemoteAppExecutionRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly RemoteAppExecutionDbContext _dbContext; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly DbSet _dataset; + private bool _disposedValue; + + // Note. this implementation (unlike the Mongo one) Does not delete the entries + // so a cleanup routine will have to be implemented to peridoically remove old entries ! + + public RemoteAppExecutionRepository( + IServiceScopeFactory serviceScopeFactory, + ILogger logger, + IOptions options) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _scope = serviceScopeFactory.CreateScope(); + _dbContext = _scope.ServiceProvider.GetRequiredService(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + _dataset = _dbContext.Set(); + } + + public async Task AddAsync(RemoteAppExecution item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + await _dataset.AddAsync(item, cancellationToken).ConfigureAwait(false); + await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return true; + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(RemoteAppExecution remoteAppExecution, CancellationToken cancellationToken = default) + { + Guard.Against.Null(remoteAppExecution, nameof(remoteAppExecution)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = _dataset.Remove(remoteAppExecution); + await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.Entity; + }).ConfigureAwait(false); + } + + public async Task GetAsync(string sopInstanceUid, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); + + return await _retryPolicy.ExecuteAsync(async () => + { + return await _dataset.SingleOrDefaultAsync(p => + p.SopInstanceUid.Equals(sopInstanceUid)).ConfigureAwait(false); + }).ConfigureAwait(false); + } + + public async Task GetAsync(string workflowInstanceId, string exportTaskId, string studyInstanceUid, string seriesInstanceUid, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); + Guard.Against.NullOrWhiteSpace(exportTaskId, nameof(exportTaskId)); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _dataset.SingleOrDefaultAsync(p => + p.WorkflowInstanceId.Equals(workflowInstanceId) && + p.ExportTaskId.Equals(exportTaskId) && + p.StudyInstanceUid.Equals(studyInstanceUid) && + p.SeriesInstanceUid.Equals(seriesInstanceUid)).ConfigureAwait(false); + + result ??= await _dataset.SingleOrDefaultAsync(p => + p.WorkflowInstanceId.Equals(workflowInstanceId) && + p.ExportTaskId.Equals(exportTaskId) && + p.StudyInstanceUid.Equals(studyInstanceUid)).ConfigureAwait(false); + + return result; + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _dbContext.Dispose(); + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/IRemoteAppExecutionRepository.cs b/src/Plug-ins/RemoteAppExecution/Database/IRemoteAppExecutionRepository.cs new file mode 100755 index 000000000..8a262053e --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/IRemoteAppExecutionRepository.cs @@ -0,0 +1,29 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database +{ + public interface IRemoteAppExecutionRepository + { + Task AddAsync(RemoteAppExecution item, CancellationToken cancellationToken = default); + + Task GetAsync(string sopInstanceUid, CancellationToken cancellationToken = default); + + Task GetAsync(string workflowInstanceId, string exportTaskId, string studyInstanceUid, string seriesInstanceUid, CancellationToken cancellationToken = default); + + Task RemoveAsync(RemoteAppExecution remoteAppExecution, CancellationToken cancellationToken = default); + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/MongoDb/MigrationManager.cs b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/MigrationManager.cs new file mode 100644 index 000000000..4d639fcf9 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/MigrationManager.cs @@ -0,0 +1,30 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Hosting; +using Monai.Deploy.InformaticsGateway.Database.Api; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb +{ + public class MigrationManager : IDatabaseMigrationManagerForPlugIns + { + public IHost Migrate(IHost host) + { + RemoteAppExecutionConfiguration.Configure(); + return host; + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionConfiguration.cs b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionConfiguration.cs new file mode 100644 index 000000000..56641afe3 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionConfiguration.cs @@ -0,0 +1,35 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * Copyright 2021 NVIDIA Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using MongoDB.Bson.Serialization; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb +{ + internal static class RemoteAppExecutionConfiguration + { + public static void Configure() + { + BsonClassMap.RegisterClassMap(j => + { + j.AutoMap(); + j.SetIdMember(j.GetMemberMap(c => c.Id)); + j.MapIdMember(c => c.Id); + j.SetIgnoreExtraElements(true); + }); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionRepository.cs b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionRepository.cs new file mode 100755 index 000000000..a4b4eaa56 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Database/MongoDb/RemoteAppExecutionRepository.cs @@ -0,0 +1,167 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ardalis.GuardClauses; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.Database.Api.Logging; +using MongoDB.Driver; +using Polly; +using Polly.Retry; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb +{ + public class RemoteAppExecutionRepository : IRemoteAppExecutionRepository, IDisposable + { + private readonly ILogger _logger; + private readonly IServiceScope _scope; + private readonly AsyncRetryPolicy _retryPolicy; + private readonly IMongoCollection _collection; + private bool _disposedValue; + + public RemoteAppExecutionRepository( + IServiceScopeFactory serviceScopeFactory, + ILoggerFactory loggerFactory, + IOptions options + ) + { + Guard.Against.Null(serviceScopeFactory, nameof(serviceScopeFactory)); + Guard.Against.Null(options, nameof(options)); + + _logger = loggerFactory.CreateLogger(); + + _scope = serviceScopeFactory.CreateScope(); + _retryPolicy = Policy.Handle().WaitAndRetryAsync( + options.Value.Retries.RetryDelays, + (exception, timespan, count, context) => _logger.DatabaseErrorRetry(timespan, count, exception)); + + var mongoDbClient = _scope.ServiceProvider.GetRequiredService(); + var mongoDatabase = mongoDbClient.GetDatabase(options.Value.DatabaseName); + _collection = mongoDatabase.GetCollection(nameof(RemoteAppExecution)); + CreateIndexes(); + } + + private void CreateIndexes() + { + var options = new CreateIndexOptions { Unique = true }; + var indexDefinitionState = Builders.IndexKeys.Ascending(_ => _.SopInstanceUid); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState, options)); + + var indexDefinitionSeriesLevel = Builders.IndexKeys.Combine( + Builders.IndexKeys.Ascending(_ => _.WorkflowInstanceId), + Builders.IndexKeys.Ascending(_ => _.ExportTaskId), + Builders.IndexKeys.Ascending(_ => _.StudyInstanceUid), + Builders.IndexKeys.Ascending(_ => _.SeriesInstanceUid)); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionSeriesLevel, options)); + + var indexDefinitionStudyLevel = Builders.IndexKeys.Combine( + Builders.IndexKeys.Ascending(_ => _.WorkflowInstanceId), + Builders.IndexKeys.Ascending(_ => _.ExportTaskId), + Builders.IndexKeys.Ascending(_ => _.StudyInstanceUid)); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionStudyLevel, options)); + + options = new CreateIndexOptions { ExpireAfter = TimeSpan.FromDays(7), Name = "RequestTime" }; + indexDefinitionState = Builders.IndexKeys.Ascending(_ => _.RequestTime); + _collection.Indexes.CreateOne(new CreateIndexModel(indexDefinitionState, options)); + } + + public async Task AddAsync(RemoteAppExecution item, CancellationToken cancellationToken = default) + { + Guard.Against.Null(item, nameof(item)); + + return await _retryPolicy.ExecuteAsync(async () => + { + await _collection.InsertOneAsync(item, cancellationToken: cancellationToken).ConfigureAwait(false); + return true; + }).ConfigureAwait(false); + } + + public async Task RemoveAsync(RemoteAppExecution remoteAppExecution, CancellationToken cancellationToken = default) + { + Guard.Against.Null(remoteAppExecution, nameof(remoteAppExecution)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.DeleteOneAsync(Builders.Filter.Where(p => p.Id == remoteAppExecution.Id), cancellationToken: cancellationToken).ConfigureAwait(false); + if (result.DeletedCount == 0) + { + throw new DatabaseException("Failed to delete entity"); + } + return remoteAppExecution; + }).ConfigureAwait(false); + } + + public async Task GetAsync(string sopInstanceUid, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(sopInstanceUid, nameof(sopInstanceUid)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.Find(p => + p.SopInstanceUid.Equals(sopInstanceUid, StringComparison.OrdinalIgnoreCase)).FirstOrDefaultAsync().ConfigureAwait(false); + + return result; + }).ConfigureAwait(false); + } + + public async Task GetAsync(string workflowInstanceId, string exportTaskId, string studyInstanceUid, string seriesInstanceUid, CancellationToken cancellationToken = default) + { + Guard.Against.NullOrWhiteSpace(workflowInstanceId, nameof(workflowInstanceId)); + Guard.Against.NullOrWhiteSpace(exportTaskId, nameof(exportTaskId)); + Guard.Against.NullOrWhiteSpace(studyInstanceUid, nameof(studyInstanceUid)); + Guard.Against.NullOrWhiteSpace(seriesInstanceUid, nameof(seriesInstanceUid)); + + return await _retryPolicy.ExecuteAsync(async () => + { + var result = await _collection.Find(p => + p.WorkflowInstanceId.Equals(workflowInstanceId, StringComparison.OrdinalIgnoreCase) && + p.ExportTaskId.Equals(exportTaskId, StringComparison.OrdinalIgnoreCase) && + p.StudyInstanceUid.Equals(studyInstanceUid, StringComparison.OrdinalIgnoreCase) && + p.SeriesInstanceUid.Equals(seriesInstanceUid, StringComparison.OrdinalIgnoreCase)).FirstOrDefaultAsync().ConfigureAwait(false); + + result ??= await _collection.Find(p => + p.WorkflowInstanceId.Equals(workflowInstanceId, StringComparison.OrdinalIgnoreCase) && + p.ExportTaskId.Equals(exportTaskId, StringComparison.OrdinalIgnoreCase) && + p.StudyInstanceUid.Equals(studyInstanceUid, StringComparison.OrdinalIgnoreCase)).FirstOrDefaultAsync().ConfigureAwait(false); + + return result; + }).ConfigureAwait(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _scope.Dispose(); + } + + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/DicomDeidentifier.cs b/src/Plug-ins/RemoteAppExecution/DicomDeidentifier.cs new file mode 100755 index 000000000..19c81aa07 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/DicomDeidentifier.cs @@ -0,0 +1,110 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using Ardalis.GuardClauses; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + [PlugInName("Remote App Execution Outgoing")] + public class DicomDeidentifier : IOutputDataPlugIn + { + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly PlugInConfiguration _options; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public DicomDeidentifier( + ILogger logger, + IServiceScopeFactory serviceScopeFactory, + IOptions configuration) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + _options = configuration?.Value ?? throw new ArgumentNullException(nameof(configuration)); + + if (!_options.RemoteAppConfigurations.ContainsKey(SR.ConfigKey_ReplaceTags)) + { + throw new ArgumentNullException(nameof(configuration)); + } + } + + public async Task<(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage)> ExecuteAsync(DicomFile dicomFile, ExportRequestDataMessage exportRequestDataMessage) + { + + Guard.Against.Null(dicomFile, nameof(dicomFile)); + Guard.Against.Null(exportRequestDataMessage, nameof(exportRequestDataMessage)); + + var tags = Utilities.GetTagArrayFromStringArray(_options.RemoteAppConfigurations[SR.ConfigKey_ReplaceTags]); + var studyInstanceUid = dicomFile.Dataset.GetSingleValue(DicomTag.StudyInstanceUID); + var seriesInstanceUid = dicomFile.Dataset.GetSingleValue(DicomTag.SeriesInstanceUID); + + + + var scope = _serviceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + + var existing = await repository.GetAsync(exportRequestDataMessage.WorkflowInstanceId, exportRequestDataMessage.ExportTaskId, studyInstanceUid, seriesInstanceUid).ConfigureAwait(false); + + var newRecord = new RemoteAppExecution(exportRequestDataMessage, existing?.StudyInstanceUid, existing?.SeriesInstanceUid) + { PayloadId = exportRequestDataMessage.FilePayloadId }; + + + newRecord.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), studyInstanceUid); + newRecord.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), seriesInstanceUid); + newRecord.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), dicomFile.Dataset.GetSingleValue(DicomTag.SOPInstanceUID)); + + dicomFile.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, newRecord.StudyInstanceUid); + dicomFile.Dataset.AddOrUpdate(DicomTag.SeriesInstanceUID, newRecord.SeriesInstanceUid); + dicomFile.Dataset.AddOrUpdate(DicomTag.SOPInstanceUID, newRecord.SopInstanceUid); + + foreach (var tag in tags) + { + if (tag.Equals(DicomTag.StudyInstanceUID) || + tag.Equals(DicomTag.SeriesInstanceUID) || + tag.Equals(DicomTag.SOPInstanceUID)) + { + continue; + } + + if (dicomFile.Dataset.TryGetString(tag, out var value)) + { + newRecord.OriginalValues.Add(tag.ToString(), value); + var newValue = Utilities.GetTagProxyValue(tag); + if (newValue != null) + { + dicomFile.Dataset.AddOrUpdate(tag, newValue); + _logger.ValueChanged(tag.ToString(), value, newValue); + } + } + } + + await repository.AddAsync(newRecord).ConfigureAwait(false); + + return (dicomFile, exportRequestDataMessage); + + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/DicomReidentifier.cs b/src/Plug-ins/RemoteAppExecution/DicomReidentifier.cs new file mode 100755 index 000000000..56634fcf6 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/DicomReidentifier.cs @@ -0,0 +1,70 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + [PlugInName("Remote App Execution Incoming")] + public class DicomReidentifier : IInputDataPlugIn + { + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public string Name => GetType().GetCustomAttribute()?.Name ?? GetType().Name; + + public DicomReidentifier( + ILogger logger, + IServiceScopeFactory serviceScopeFactory) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory)); + } + + public async Task<(DicomFile dicomFile, FileStorageMetadata fileMetadata)> ExecuteAsync(DicomFile dicomFile, FileStorageMetadata fileMetadata) + { + var scope = _serviceScopeFactory.CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService(); + + var sopInstanceUid = dicomFile.Dataset.GetSingleValue(DicomTag.SOPInstanceUID); + var remoteAppExecution = await repository.GetAsync(sopInstanceUid).ConfigureAwait(false); + + if (remoteAppExecution is null) + { + _logger.IncomingInstanceNotFound(sopInstanceUid); + return (dicomFile, fileMetadata); + } + + foreach (var key in remoteAppExecution.OriginalValues.Keys) + { + dicomFile.Dataset.AddOrUpdate(DicomTag.Parse(key), remoteAppExecution.OriginalValues[key]); + } + + fileMetadata.WorkflowInstanceId = remoteAppExecution.WorkflowInstanceId; + fileMetadata.TaskId = remoteAppExecution.ExportTaskId; + fileMetadata.ChangeCorrelationId(_logger, remoteAppExecution.CorrelationId); + fileMetadata.PayloadId = remoteAppExecution.PayloadId; + + return (dicomFile, fileMetadata); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/InternalVisibleTo.cs b/src/Plug-ins/RemoteAppExecution/InternalVisibleTo.cs new file mode 100644 index 000000000..ee5c4f7ae --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/InternalVisibleTo.cs @@ -0,0 +1,19 @@ +/* + * Copyright 2021-2022 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test")] diff --git a/src/Plug-ins/RemoteAppExecution/Log.10000.DataPlugins.cs b/src/Plug-ins/RemoteAppExecution/Log.10000.DataPlugins.cs new file mode 100644 index 000000000..50b1c4b76 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Log.10000.DataPlugins.cs @@ -0,0 +1,32 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Logging; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + public static partial class Log + { + [LoggerMessage(EventId = 10000, Level = LogLevel.Debug, Message = "Changed {tag} from {originalValue} to {newValue}.")] + public static partial void ValueChanged(this ILogger logger, string tag, string originalValue, string newValue); + + [LoggerMessage(EventId = 10001, Level = LogLevel.Error, Message = "Cannot find entry for incoming instance {sopInstanceUid}.")] + public static partial void IncomingInstanceNotFound(this ILogger logger, string sopInstanceUid); + + [LoggerMessage(EventId = 11002, Level = LogLevel.Debug, Message = "Added scoped service for DbType of {repo} for type {DbType}.")] + public static partial void AddedDbScope(this ILogger logger, string repo, string DbType); + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.Designer.cs b/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.Designer.cs new file mode 100644 index 000000000..89ad68f62 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.Designer.cs @@ -0,0 +1,72 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Migrations +{ + [DbContext(typeof(RemoteAppExecutionDbContext))] + [Migration("20230818161328_R4_0.4.0")] + partial class R4_040 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.21"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.RemoteAppExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExportTaskId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OriginalValues") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RequestTime") + .HasColumnType("TEXT"); + + b.Property("SeriesInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SopInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid", "SeriesInstanceUid" }, "idx_remoteapp_all"); + + b.HasIndex(new[] { "SopInstanceUid" }, "idx_remoteapp_instance"); + + b.HasIndex(new[] { "WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid" }, "idx_remoteapp_study"); + + b.ToTable("RemoteAppExecutions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.cs b/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.cs new file mode 100755 index 000000000..60d1e7d4e --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Migrations/20230818161328_R4_0.4.0.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Migrations +{ + public partial class R4_040 : Migration + { + private static readonly string[] Columns = ["WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid"]; + private static readonly string[] StudyColumns = ["WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid", "SeriesInstanceUid"]; + + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RemoteAppExecutions", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + RequestTime = table.Column(type: "TEXT", nullable: false), + WorkflowInstanceId = table.Column(type: "TEXT", nullable: false), + ExportTaskId = table.Column(type: "TEXT", nullable: false), + CorrelationId = table.Column(type: "TEXT", nullable: false), + StudyInstanceUid = table.Column(type: "TEXT", nullable: false), + SeriesInstanceUid = table.Column(type: "TEXT", nullable: false), + SopInstanceUid = table.Column(type: "TEXT", nullable: false), + OriginalValues = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_RemoteAppExecutions", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "idx_remoteapp_all", + table: "RemoteAppExecutions", + columns: StudyColumns); + + migrationBuilder.CreateIndex( + name: "idx_remoteapp_instance", + table: "RemoteAppExecutions", + column: "SopInstanceUid"); + + migrationBuilder.CreateIndex( + name: "idx_remoteapp_study", + table: "RemoteAppExecutions", + columns: Columns); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RemoteAppExecutions"); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Migrations/RemoteAppExecutionDbContextModelSnapshot.cs b/src/Plug-ins/RemoteAppExecution/Migrations/RemoteAppExecutionDbContextModelSnapshot.cs new file mode 100644 index 000000000..cecd44f2c --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Migrations/RemoteAppExecutionDbContextModelSnapshot.cs @@ -0,0 +1,70 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; + +#nullable disable + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Migrations +{ + [DbContext(typeof(RemoteAppExecutionDbContext))] + partial class RemoteAppExecutionDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.21"); + + modelBuilder.Entity("Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.RemoteAppExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ExportTaskId") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OriginalValues") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RequestTime") + .HasColumnType("TEXT"); + + b.Property("SeriesInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SopInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("StudyInstanceUid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("WorkflowInstanceId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid", "SeriesInstanceUid" }, "idx_remoteapp_all"); + + b.HasIndex(new[] { "SopInstanceUid" }, "idx_remoteapp_instance"); + + b.HasIndex(new[] { "WorkflowInstanceId", "ExportTaskId", "StudyInstanceUid" }, "idx_remoteapp_study"); + + b.ToTable("RemoteAppExecutions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.csproj b/src/Plug-ins/RemoteAppExecution/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.csproj new file mode 100755 index 000000000..78d213df0 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.csproj @@ -0,0 +1,57 @@ + + + + Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution + net8.0 + enable + enable + Apache-2.0 + true + True + latest + ..\..\.sonarlint\project-monai_monai-deploy-informatics-gatewaycsharp.ruleset + false + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plug-ins/RemoteAppExecution/RemoteAppExecution.cs b/src/Plug-ins/RemoteAppExecution/RemoteAppExecution.cs new file mode 100755 index 000000000..fee0722da --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/RemoteAppExecution.cs @@ -0,0 +1,87 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.Json.Serialization; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Models; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + public class RemoteAppExecution + { + /// + /// Gets the ID of this record. + /// + [JsonPropertyName("_id")] + public Guid Id { get; init; } = Guid.NewGuid(); + + /// + /// Gets the date time this record is created. + /// + public DateTimeOffset RequestTime { get; init; } = DateTime.UtcNow; + + /// + /// Gets or sets the workflow instance ID of the original request. + /// + public string WorkflowInstanceId { get; set; } = string.Empty; + + /// + /// Gets or sets the export task ID of the original request. + /// + public string ExportTaskId { get; set; } = string.Empty; + + public string? PayloadId { get; set; } + + /// + /// Gets or sets the correlation ID of the original request. + /// + public string CorrelationId { get; set; } = string.Empty; + + ///// + ///// Gets or sets the proxy value of Study Instance UID. + ///// + public string StudyInstanceUid { get; set; } = string.Empty; + + ///// + ///// Gets or sets the proxy value of Series Instance UID. + ///// + public string SeriesInstanceUid { get; set; } = string.Empty; + + ///// + ///// Gets or sets the proxy value of SOP Instance UID. + ///// + public string SopInstanceUid { get; set; } = string.Empty; + + /// + /// Gets or sets the original values of a given DICOM tag. + /// + public Dictionary OriginalValues { get; init; } = new(); + + public RemoteAppExecution() + { } + + public RemoteAppExecution(ExportRequestDataMessage exportRequestDataMessage, string? studyInstanceUid, string? seriesInstanceUid) + { + WorkflowInstanceId = exportRequestDataMessage.WorkflowInstanceId; + ExportTaskId = exportRequestDataMessage.ExportTaskId; + CorrelationId = exportRequestDataMessage.CorrelationId; + + StudyInstanceUid = studyInstanceUid ?? Utilities.GetTagProxyValue(DicomTag.StudyInstanceUID) ?? ""; + SeriesInstanceUid = seriesInstanceUid ?? Utilities.GetTagProxyValue(DicomTag.SeriesInstanceUID) ?? ""; + SopInstanceUid = Utilities.GetTagProxyValue(DicomTag.SOPInstanceUID) ?? ""; + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/SR.cs b/src/Plug-ins/RemoteAppExecution/SR.cs new file mode 100644 index 000000000..09cbe4b45 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/SR.cs @@ -0,0 +1,27 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + internal static class SR + { + public const string ConfigKey_ReplaceTags = "ReplaceTags"; + + public const string DatabaseConnectionStringKey = "InformaticsGatewayDatabase"; + + public const string DatabaseNameKey = "DatabaseName"; + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/DatabaseRegistrarTest.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/DatabaseRegistrarTest.cs new file mode 100755 index 000000000..2a8473ec9 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/DatabaseRegistrarTest.cs @@ -0,0 +1,95 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Database.Api; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; +using Moq; +using Xunit; +using MongoDbTypes = Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database +{ + public class DatabaseRegistrarTest + { + [Fact] + public void GivenEntityFrameworkDatabaseType_WhenConfigureIsCalled_AddsDependencies() + { + var serviceDescriptors = new List(); + var serviceCollection = new Mock(); + serviceCollection.Setup(p => p.Add(It.IsAny())); + serviceCollection.Setup(p => p.GetEnumerator()).Returns(serviceDescriptors.GetEnumerator()); + + var registrar = new DatabaseRegistrar(); + var configInMemory = new List> { + new("top:InformaticsGatewayDatabase","DataSource=file::memory:?cache=shared"), + }; + + IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(configInMemory).Build(); + + var loggerMock = new Mock(); + var loggerFactoryMock = new Mock(); + loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(loggerMock.Object); + + var returnedServiceCollection = registrar.Configure( + serviceCollection.Object, + DatabaseType.EntityFramework, + configuration.GetSection("top"), + configuration.GetSection("top"), loggerFactoryMock.Object); + + Assert.Same(serviceCollection.Object, returnedServiceCollection); + + serviceCollection.Verify(p => p.Add(It.IsAny()), Times.Exactly(6)); + serviceCollection.Verify(p => p.Add(It.Is(p => p.ServiceType == typeof(RemoteAppExecutionDbContext))), Times.Once()); + serviceCollection.Verify(p => p.Add(It.Is(p => p.ServiceType == typeof(IDatabaseMigrationManagerForPlugIns) && p.ImplementationType == typeof(MigrationManager))), Times.Once()); + serviceCollection.Verify(p => p.Add(It.Is(p => p.ServiceType == typeof(IRemoteAppExecutionRepository) && p.ImplementationType == typeof(RemoteAppExecutionRepository))), Times.Once()); + } + + [Fact] + public void GivenMongoDatabaseType_WhenConfigureIsCalled_AddsDependencies() + { + var serviceDescriptors = new List(); + var serviceCollection = new Mock(); + serviceCollection.Setup(p => p.Add(It.IsAny())); + serviceCollection.Setup(p => p.GetEnumerator()).Returns(serviceDescriptors.GetEnumerator()); + + var registrar = new DatabaseRegistrar(); + var configInMemory = new List> { + new("top:InformaticsGatewayDatabase","DataSource=file::memory:?cache=shared"), + }; + + var loggerMock = new Mock(); + var loggerFactoryMock = new Mock(); + loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())).Returns(loggerMock.Object); + + IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(configInMemory).Build(); + var returnedServiceCollection = registrar.Configure( + serviceCollection.Object, + DatabaseType.MongoDb, + configuration.GetSection("top"), + configuration.GetSection("top"), + loggerFactoryMock.Object); + + Assert.Same(serviceCollection.Object, returnedServiceCollection); + + serviceCollection.Verify(p => p.Add(It.Is(p => p.ServiceType == typeof(IDatabaseMigrationManagerForPlugIns) && p.ImplementationType == typeof(MongoDbTypes.MigrationManager))), Times.Once()); + serviceCollection.Verify(p => p.Add(It.Is(p => p.ServiceType == typeof(IRemoteAppExecutionRepository) && p.ImplementationType == typeof(MongoDbTypes.RemoteAppExecutionRepository))), Times.Once()); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/MigrationManagerTest.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/MigrationManagerTest.cs new file mode 100644 index 000000000..6c7a95a71 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/MigrationManagerTest.cs @@ -0,0 +1,57 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database.EntityFramework +{ + public class MigrationManagerTest + { + private readonly Mock _host; + private readonly RemoteAppExecutionDbContext _dbContext; + private readonly IServiceProvider _serviceProvider; + + public MigrationManagerTest() + { + var options = new DbContextOptionsBuilder() + .UseSqlite("DataSource=file:memdbmigration?mode=memory&cache=shared") + .Options; + + _host = new Mock(); + _dbContext = new RemoteAppExecutionDbContext(options); + + var services = new ServiceCollection(); + services.AddScoped(p => _dbContext); + + _serviceProvider = services.BuildServiceProvider(); + _host.Setup(p => p.Services).Returns(_serviceProvider); + } + + [Fact] + public void GivenARemoteAppExecutionDbContext_OnMigration_MigratesSuccessfully() + { + var mgr = new MigrationManager(); + var result = mgr.Migrate(_host.Object); + + Assert.Same(_host.Object, result); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/RemoteAppExecutionRepositoryTest.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/RemoteAppExecutionRepositoryTest.cs new file mode 100755 index 000000000..2d02f8fa6 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/RemoteAppExecutionRepositoryTest.cs @@ -0,0 +1,169 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FellowOakDicom; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database.EntityFramework +{ + [Collection("SqliteDatabase")] + public class RemoteAppExecutionRepositoryTest + { + private readonly SqliteDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock> _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public RemoteAppExecutionRepositoryTest(SqliteDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithRemoteAppExecutions(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock>(); + _options = Options.Create(new DatabaseOptions()); + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.DatabaseContext); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenAddingToDatabase_ExpectItToBeSaved() + { + var record = new RemoteAppExecution + { + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + Id = Guid.NewGuid(), + RequestTime = DateTimeOffset.UtcNow, + StudyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SeriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + }; + + record.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.PatientID.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.AccessionNumber.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.StudyDescription.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(record).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + var actual = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Id.Equals(record.Id)).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(record.CorrelationId, actual!.CorrelationId); + Assert.Equal(record.ExportTaskId, actual!.ExportTaskId); + Assert.Equal(record.Id, actual!.Id); + Assert.Equal(record.RequestTime, actual!.RequestTime); + Assert.Equal(record.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var record = _databaseFixture.RemoteAppExecutions.First(); + var expected = await store.GetAsync(record.SopInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var dbResult = await _databaseFixture.DatabaseContext.Set().FirstOrDefaultAsync(p => p.Id == record.Id).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithSopInstanceUid_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.SopInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithStudyAndSeriesUids_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.WorkflowInstanceId, expected.ExportTaskId, expected.StudyInstanceUid, expected.SeriesInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithRandomSeries_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.WorkflowInstanceId, expected.ExportTaskId, expected.StudyInstanceUid, DicomUIDGenerator.GenerateDerivedFromUUID().UID).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/SqliteDatabaseFixture.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/SqliteDatabaseFixture.cs new file mode 100644 index 000000000..697916608 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/EntityFramework/SqliteDatabaseFixture.cs @@ -0,0 +1,87 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FellowOakDicom; +using Microsoft.EntityFrameworkCore; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.EntityFramework; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database.EntityFramework +{ + [CollectionDefinition("SqliteDatabase")] + public class SqliteDatabaseCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } + + public class SqliteDatabaseFixture + { + public RemoteAppExecutionDbContext DatabaseContext { get; set; } + public IList RemoteAppExecutions { get; init; } + + public SqliteDatabaseFixture() + { + var options = new DbContextOptionsBuilder() + .UseSqlite("DataSource=file::memory:?cache=shared") + .Options; + DatabaseContext = new RemoteAppExecutionDbContext(options); + DatabaseContext.Database.EnsureCreated(); + + RemoteAppExecutions = new List(); + } + + public void Dispose() + { + DatabaseContext.Dispose(); + } + + internal void InitDatabaseWithRemoteAppExecutions() + { + var set = DatabaseContext.Set(); + set.RemoveRange(set.ToList()); + RemoteAppExecutions.Clear(); + for (var i = 0; i < 5; i++) + { + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var record = new RemoteAppExecution + { + WorkflowInstanceId = Guid.NewGuid().ToString(), + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + Id = Guid.NewGuid(), + RequestTime = DateTimeOffset.UtcNow, + StudyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SeriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + }; + + record.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), studyInstanceUid); + record.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.PatientID.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.AccessionNumber.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.StudyDescription.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + + set.Add(record); + RemoteAppExecutions.Add(record); + } + + DatabaseContext.SaveChanges(); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/MongoDatabaseFixture.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/MongoDatabaseFixture.cs new file mode 100755 index 000000000..e9bc9249d --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/MongoDatabaseFixture.cs @@ -0,0 +1,90 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FellowOakDicom; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb; +using MongoDB.Driver; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database.MongoDb +{ + [CollectionDefinition("MongoDatabase")] + public class MongoDatabaseCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } + + public class MongoDatabaseFixture + { + public IMongoClient Client { get; set; } + public IMongoDatabase Database { get; set; } + public IOptions Options { get; set; } + public IList RemoteAppExecutions { get; init; } + + public MongoDatabaseFixture() + { + Client = new MongoClient("mongodb://root:rootpassword@localhost:27017"); + Options = Microsoft.Extensions.Options.Options.Create(new DatabaseOptions { DatabaseName = $"IGTest" }); + Database = Client.GetDatabase(Options.Value.DatabaseName); + + var migration = new MigrationManager(); + migration.Migrate(null!); + + RemoteAppExecutions = new List(); + } + + internal void InitDatabaseWithRemoteAppExecutions() + { + var collection = Database.GetCollection(nameof(RemoteAppExecution)); + Clear(collection); + RemoteAppExecutions.Clear(); + for (var i = 0; i < 5; i++) + { + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var record = new RemoteAppExecution + { + WorkflowInstanceId = Guid.NewGuid().ToString(), + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + Id = Guid.NewGuid(), + RequestTime = DateTimeOffset.UtcNow, + StudyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SeriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + }; + + record.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), studyInstanceUid); + record.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.PatientID.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.AccessionNumber.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.StudyDescription.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + + collection.InsertOne(record); + RemoteAppExecutions.Add(record); + } + } + + public static void Clear(IMongoCollection collection) where T : class + { + collection.DeleteMany(Builders.Filter.Empty); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/RemoteAppExecutionRepositoryTest.cs b/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/RemoteAppExecutionRepositoryTest.cs new file mode 100755 index 000000000..028b93910 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Database/MongoDb/RemoteAppExecutionRepositoryTest.cs @@ -0,0 +1,172 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using FellowOakDicom; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database.MongoDb; +using MongoDB.Driver; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.Database.MongoDb +{ + [Collection("MongoDatabase")] + public class RemoteAppExecutionRepositoryTest + { + private readonly MongoDatabaseFixture _databaseFixture; + + private readonly Mock _serviceScopeFactory; + private readonly Mock _logger; + private readonly IOptions _options; + + private readonly Mock _serviceScope; + private readonly IServiceProvider _serviceProvider; + + public RemoteAppExecutionRepositoryTest(MongoDatabaseFixture databaseFixture) + { + _databaseFixture = databaseFixture ?? throw new ArgumentNullException(nameof(databaseFixture)); + _databaseFixture.InitDatabaseWithRemoteAppExecutions(); + + _serviceScopeFactory = new Mock(); + _logger = new Mock(); + _options = _databaseFixture.Options; + + _serviceScope = new Mock(); + var services = new ServiceCollection(); + services.AddScoped(p => _logger.Object); + services.AddScoped(p => databaseFixture.Client); + + _serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _options.Value.Retries.DelaysMilliseconds = new[] { 1, 1, 1 }; + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenAddingToDatabase_ExpectItToBeSaved() + { + var record = new RemoteAppExecution + { + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + Id = Guid.NewGuid(), + RequestTime = DateTimeOffset.UtcNow, + StudyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SeriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + SopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID, + }; + + record.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.PatientID.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.AccessionNumber.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.StudyDescription.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + await store.AddAsync(record).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + var collection = _databaseFixture.Database.GetCollection(nameof(RemoteAppExecution)); + var actual = await collection.Find(p => p.Id == record.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + Assert.NotNull(actual); + Assert.Equal(record.CorrelationId, actual!.CorrelationId); + Assert.Equal(record.ExportTaskId, actual!.ExportTaskId); + Assert.Equal(record.Id, actual!.Id); + Assert.Equal(record.RequestTime, actual!.RequestTime); + Assert.Equal(record.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenRemoveIsCalled_ExpectItToDeleted() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var record = _databaseFixture.RemoteAppExecutions.First(); + var expected = await store.GetAsync(record.SopInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(expected); + + var actual = await store.RemoveAsync(expected!).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Same(expected, actual); + + var collection = _databaseFixture.Database.GetCollection(nameof(RemoteAppExecution)); + var dbResult = await collection.Find(p => p.Id == record.Id).FirstOrDefaultAsync().ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.Null(dbResult); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithSopInstanceUid_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.SopInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithStudyAndSeriesUids_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.WorkflowInstanceId, expected.ExportTaskId, expected.StudyInstanceUid, expected.SeriesInstanceUid).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + + [Fact] + public async Task GivenARemoteAppExecution_WhenGetAsyncIsCalledWithRandomSeries_ExpectItToBeReturned() + { + var store = new RemoteAppExecutionRepository(_serviceScopeFactory.Object, _logger.Object, _options); + + var expected = _databaseFixture.RemoteAppExecutions.First(); + var actual = await store.GetAsync(expected.WorkflowInstanceId, expected.ExportTaskId, expected.StudyInstanceUid, DicomUIDGenerator.GenerateDerivedFromUUID().UID).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Assert.NotNull(actual); + Assert.Equal(expected.SopInstanceUid, actual.SopInstanceUid); + Assert.Equal(expected.StudyInstanceUid, actual.StudyInstanceUid); + Assert.Equal(expected.SeriesInstanceUid, actual.SeriesInstanceUid); + Assert.Equal(expected.WorkflowInstanceId, actual.WorkflowInstanceId); + Assert.Equal(expected.ExportTaskId, actual.ExportTaskId); + Assert.Equal(expected.RequestTime, actual.RequestTime); + Assert.Equal(expected.Id, actual.Id); + Assert.Equal(expected.CorrelationId, actual.CorrelationId); + Assert.Equal(expected.OriginalValues, actual.OriginalValues); + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/DicomDeidentifierTest.cs b/src/Plug-ins/RemoteAppExecution/Test/DicomDeidentifierTest.cs new file mode 100755 index 000000000..4053496c5 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/DicomDeidentifierTest.cs @@ -0,0 +1,265 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test +{ + public class DicomDeidentifierTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly ServiceCollection _serviceCollection; + private readonly Mock _repository; + private readonly IOptions _options; + private readonly Mock _serviceScope; + private readonly ServiceProvider _serviceProvider; + + public DicomDeidentifierTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _repository = new Mock(); + _serviceScope = new Mock(); + _options = Options.Create(new PlugInConfiguration()); + + _serviceCollection = new ServiceCollection(); + _serviceCollection.AddScoped(p => _logger.Object); + _serviceCollection.AddScoped(p => _repository.Object); + + _serviceProvider = _serviceCollection.BuildServiceProvider(); + + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenDicomDeidentifier_TestConstructors() + { +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + Assert.Throws(() => new DicomDeidentifier(null, null, null)); + + Assert.Throws(() => new DicomDeidentifier(_logger.Object, null, null)); + Assert.Throws(() => new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, null)); + Assert.Throws(() => new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options)); +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + _options.Value.RemoteAppConfigurations.Add(SR.ConfigKey_ReplaceTags, "tag1, tag2"); + var app = new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options); + + Assert.Equal(app.Name, app.GetType().GetCustomAttribute()!.Name); + } + + [Fact] + public async Task GivenEmptyReplaceTags_WhenExecuteIsCalledWithoutExistingRecords_ExpectAsync() + { + _options.Value.RemoteAppConfigurations.Add(SR.ConfigKey_ReplaceTags, string.Empty); + var app = new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var exportRequest = GenerateExportRequest(); + var message = new ExportRequestDataMessage(exportRequest, "file.dcm"); + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + _ = await app.ExecuteAsync(dicom, message).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync( + It.Is(p => p == exportRequest.WorkflowInstanceId), + It.Is(p => p == exportRequest.ExportTaskId), + It.Is(p => p == studyInstanceUid), + It.Is(p => p == seriesInstanceUid), + It.IsAny()), Times.Once()); + + _repository.Verify(p => p.AddAsync( + It.Is(p => AssertRecord(p, dicom, exportRequest, studyInstanceUid, seriesInstanceUid, sopInstanceUid)), + It.IsAny()), Times.Once()); + } + + [Fact] + public async Task GivenReplaceTags_WhenExecuteIsCalledWithoutExistingRecords_ExpectAsync() + { + _options.Value.RemoteAppConfigurations.Add(SR.ConfigKey_ReplaceTags, "StudyInstanceUID,AccessionNumber,PatientID,PatientName"); + + var app = new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var accessionNumber = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); + var patientId = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); + var patientName = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); + var exportRequest = GenerateExportRequest(); + var message = new ExportRequestDataMessage(exportRequest, "file.dcm"); + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + dicom.Dataset.AddOrUpdate(DicomTag.AccessionNumber, accessionNumber); + dicom.Dataset.AddOrUpdate(DicomTag.PatientID, patientId); + dicom.Dataset.AddOrUpdate(DicomTag.PatientName, patientName); + + _ = await app.ExecuteAsync(dicom, message).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync( + It.Is(p => p == exportRequest.WorkflowInstanceId), + It.Is(p => p == exportRequest.ExportTaskId), + It.Is(p => p == studyInstanceUid), + It.Is(p => p == seriesInstanceUid), + It.IsAny()), Times.Once()); + + _repository.Verify(p => p.AddAsync( + It.Is(p => AssertRecordWithAdditionalTags(p, dicom, exportRequest, studyInstanceUid, seriesInstanceUid, sopInstanceUid, accessionNumber, patientId, patientName)), + It.IsAny()), Times.Once()); + } + + [Fact] + public async Task GivenExistingRecordWithSameStudy_WhenExecuteIsCalled_ExpectAsync() + { + _options.Value.RemoteAppConfigurations.Add(SR.ConfigKey_ReplaceTags, string.Empty); + var app = new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var exportRequest = GenerateExportRequest(); + var message = new ExportRequestDataMessage(exportRequest, "file.dcm"); + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + _repository.Setup(p => p.GetAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new RemoteAppExecution + { + WorkflowInstanceId = exportRequest.WorkflowInstanceId, + ExportTaskId = exportRequest.ExportTaskId, + StudyInstanceUid = studyInstanceUid, + SeriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID + }); + + _ = await app.ExecuteAsync(dicom, message).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync( + It.Is(p => p == exportRequest.WorkflowInstanceId), + It.Is(p => p == exportRequest.ExportTaskId), + It.Is(p => p == studyInstanceUid), + It.Is(p => p == seriesInstanceUid), + It.IsAny()), Times.Once()); + + _repository.Verify(p => p.AddAsync( + It.Is(p => AssertRecord(p, dicom, exportRequest, studyInstanceUid, seriesInstanceUid, sopInstanceUid)), + It.IsAny()), Times.Once()); + } + + [Fact] + public async Task GivenExistingRecordWithSameSeries_WhenExecuteIsCalled_ExpectAsync() + { + _options.Value.RemoteAppConfigurations.Add(SR.ConfigKey_ReplaceTags, string.Empty); + var app = new DicomDeidentifier(_logger.Object, _serviceScopeFactory.Object, _options); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var exportRequest = GenerateExportRequest(); + var message = new ExportRequestDataMessage(exportRequest, "file.dcm"); + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + _repository.Setup(p => p.GetAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(new RemoteAppExecution + { + WorkflowInstanceId = exportRequest.WorkflowInstanceId, + ExportTaskId = exportRequest.ExportTaskId, + StudyInstanceUid = studyInstanceUid, + SeriesInstanceUid = seriesInstanceUid + }); + + _ = await app.ExecuteAsync(dicom, message).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync( + It.Is(p => p == exportRequest.WorkflowInstanceId), + It.Is(p => p == exportRequest.ExportTaskId), + It.Is(p => p == studyInstanceUid), + It.Is(p => p == seriesInstanceUid), + It.IsAny()), Times.Once()); + + _repository.Verify(p => p.AddAsync( + It.Is(p => AssertRecord(p, dicom, exportRequest, studyInstanceUid, seriesInstanceUid, sopInstanceUid)), + It.IsAny()), Times.Once()); + } + + private bool AssertRecord( + RemoteAppExecution record, + DicomFile dicom, + ExportRequestEvent exportRequest, + string studyInstanceUid, + string seriesInstanceUid, + string sopInstanceUid) + { + return record.WorkflowInstanceId == exportRequest.WorkflowInstanceId && + record.ExportTaskId == exportRequest.ExportTaskId && + record.OriginalValues[DicomTag.StudyInstanceUID.ToString()] == studyInstanceUid && + record.OriginalValues[DicomTag.SeriesInstanceUID.ToString()] == seriesInstanceUid && + record.OriginalValues[DicomTag.SOPInstanceUID.ToString()] == sopInstanceUid && + + record.StudyInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.StudyInstanceUID) && + record.SeriesInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.SeriesInstanceUID) && + record.SopInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.SOPInstanceUID); + } + + private bool AssertRecordWithAdditionalTags( + RemoteAppExecution record, + DicomFile dicom, + ExportRequestEvent exportRequest, + string studyInstanceUid, + string seriesInstanceUid, + string sopInstanceUid, + string accessionNumber, + string patientId, + string patientName) + { + return record.WorkflowInstanceId == exportRequest.WorkflowInstanceId && + record.ExportTaskId == exportRequest.ExportTaskId && + record.OriginalValues[DicomTag.StudyInstanceUID.ToString()] == studyInstanceUid && + record.OriginalValues[DicomTag.SeriesInstanceUID.ToString()] == seriesInstanceUid && + record.OriginalValues[DicomTag.SOPInstanceUID.ToString()] == sopInstanceUid && + record.OriginalValues[DicomTag.AccessionNumber.ToString()] == accessionNumber && + record.OriginalValues[DicomTag.PatientID.ToString()] == patientId && + record.OriginalValues[DicomTag.PatientName.ToString()] == patientName && + + record.StudyInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.StudyInstanceUID) && + record.SeriesInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.SeriesInstanceUID) && + record.SopInstanceUid == dicom.Dataset.GetSingleValue(DicomTag.SOPInstanceUID); + } + + private ExportRequestEvent GenerateExportRequest() => + new() + { + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + PayloadId = null + }; + } +} diff --git a/src/Plug-ins/RemoteAppExecution/Test/DicomReidentifierTest.cs b/src/Plug-ins/RemoteAppExecution/Test/DicomReidentifierTest.cs new file mode 100644 index 000000000..494dda53d --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/DicomReidentifierTest.cs @@ -0,0 +1,137 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Reflection; +using FellowOakDicom; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Api.PlugIns; +using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Database; +using Monai.Deploy.InformaticsGateway.SharedTest; +using Monai.Deploy.Messaging.Events; +using Moq; +using Xunit; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test +{ + public class DicomReidentifierTest + { + private readonly Mock> _logger; + private readonly Mock _serviceScopeFactory; + private readonly ServiceCollection _serviceCollection; + private readonly Mock _repository; + private readonly Mock _serviceScope; + private readonly ServiceProvider _serviceProvider; + + public DicomReidentifierTest() + { + _logger = new Mock>(); + _serviceScopeFactory = new Mock(); + _repository = new Mock(); + _serviceScope = new Mock(); + + _serviceCollection = new ServiceCollection(); + _serviceCollection.AddScoped(p => _logger.Object); + _serviceCollection.AddScoped(p => _repository.Object); + + _serviceProvider = _serviceCollection.BuildServiceProvider(); + + _serviceScopeFactory.Setup(p => p.CreateScope()).Returns(_serviceScope.Object); + _serviceScope.Setup(p => p.ServiceProvider).Returns(_serviceProvider); + + _logger.Setup(p => p.IsEnabled(It.IsAny())).Returns(true); + } + + [Fact] + public void GivenDicomDeidentifier_TestConstructors() + { +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + Assert.Throws(() => new DicomReidentifier(null, null)); + + Assert.Throws(() => new DicomReidentifier(_logger.Object, null)); + + var app = new DicomReidentifier(_logger.Object, _serviceScopeFactory.Object); + + Assert.Equal(app.Name, app.GetType().GetCustomAttribute()!.Name); + } + + [Fact] + public async Task GivenIncomingInstance_WhenExecuteIsCalledWithMissingRecord_ExpectErrorToBeLogged() + { + var app = new DicomReidentifier(_logger.Object, _serviceScopeFactory.Object); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + var metadata = new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), studyInstanceUid, seriesInstanceUid, sopInstanceUid, DataService.DIMSE, "calling", "called"); + + _repository.Setup(p => p.GetAsync(It.IsAny(), It.IsAny())).ReturnsAsync(default(RemoteAppExecution)); + + _ = await app.ExecuteAsync(dicom, metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync(It.IsAny(), It.IsAny()), Times.Once()); + + _logger.VerifyLogging($"Cannot find entry for incoming instance {sopInstanceUid}.", LogLevel.Error, Times.Once()); + } + + [Fact] + public async Task GivenIncomingInstance_WhenExecuteIsCalledWithRecord_ExpectDataToBeFilled() + { + var app = new DicomReidentifier(_logger.Object, _serviceScopeFactory.Object); + + var studyInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var seriesInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var sopInstanceUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + var dicom = InstanceGenerator.GenerateDicomFile(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + var metadata = new DicomFileStorageMetadata(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), studyInstanceUid, seriesInstanceUid, sopInstanceUid, DataService.DIMSE, "calling", "called"); + var record = new RemoteAppExecution + { + CorrelationId = Guid.NewGuid().ToString(), + ExportTaskId = Guid.NewGuid().ToString(), + Id = Guid.NewGuid(), + RequestTime = DateTimeOffset.UtcNow, + }; + + record.OriginalValues.Add(DicomTag.StudyInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SeriesInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.SOPInstanceUID.ToString(), DicomUIDGenerator.GenerateDerivedFromUUID().UID); + record.OriginalValues.Add(DicomTag.PatientID.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.AccessionNumber.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + record.OriginalValues.Add(DicomTag.StudyDescription.ToString(), Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16)); + + _repository.Setup(p => p.GetAsync(It.IsAny(), It.IsAny())).ReturnsAsync(record); + + _ = await app.ExecuteAsync(dicom, metadata).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + _repository.Verify(p => p.GetAsync(It.IsAny(), It.IsAny()), Times.Once()); + + _logger.VerifyLogging($"Cannot find entry for incoming instance {sopInstanceUid}.", LogLevel.Error, Times.Never()); + + Assert.Equal(record.OriginalValues[DicomTag.StudyInstanceUID.ToString()], dicom.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)); + Assert.Equal(record.OriginalValues[DicomTag.SeriesInstanceUID.ToString()], dicom.Dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty)); + Assert.Equal(record.OriginalValues[DicomTag.PatientID.ToString()], dicom.Dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty)); + Assert.Equal(record.OriginalValues[DicomTag.AccessionNumber.ToString()], dicom.Dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty)); + Assert.Equal(record.OriginalValues[DicomTag.StudyDescription.ToString()], dicom.Dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty)); + + Assert.Equal(record.CorrelationId, metadata.CorrelationId); + Assert.Equal(record.ExportTaskId, metadata.TaskId); + Assert.Equal(record.WorkflowInstanceId, metadata.WorkflowInstanceId); + } + } +} +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. diff --git a/src/Plug-ins/RemoteAppExecution/Test/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.csproj b/src/Plug-ins/RemoteAppExecution/Test/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.csproj new file mode 100755 index 000000000..c55b71e5b --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test.csproj @@ -0,0 +1,57 @@ + + + + net8.0 + enable + enable + Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test + false + Apache-2.0 + true + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json b/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json new file mode 100644 index 000000000..97e3b15c1 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Test/packages.lock.json @@ -0,0 +1,924 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.EntityFrameworkCore.InMemory": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "q0/yGN42dy/ph9tV1ecuvrzpfkGuGdjyfdWNSXylQ0CDUidBCG6o+srOyOPg32gbjV0nLRv9YFkrS4xprXwAew==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "8.0.14" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", + "dependencies": { + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "System.IO.Abstractions.TestingHelpers": { + "type": "Direct", + "requested": "[21.3.1, )", + "resolved": "21.3.1", + "contentHash": "LsvGSS5XbvVonvWo1fb6X5T3WFAe59A9A+F+KiyXXDEw8FDDEvaoI+IbUzLPyjTZJ1sIFredoJCgppXjEESL4A==", + "dependencies": { + "TestableIO.System.IO.Abstractions.TestingHelpers": "21.3.1" + } + }, + "xRetry": { + "type": "Direct", + "requested": "[1.9.0, )", + "resolved": "1.9.0", + "contentHash": "NeIbJrwpc5EUPagx/mdd/7KzpR36BO8IWrsbgtvOVjxD2xtmNfUHieZ24PeZ4oCYiLBcTviCy+og/bE/OvPchw==", + "dependencies": { + "xunit.core": "[2.4.0, 3.0.0)" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", + "dependencies": { + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" + }, + "Ardalis.GuardClauses": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", + "dependencies": { + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "fo-dicom": { + "type": "Transitive", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" + } + }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Macross.Json.Extensions": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.13.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Monai.Deploy.Messaging": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" + } + }, + "Monai.Deploy.Storage": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Storage.S3Policy": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", + "dependencies": { + "System.CodeDom": "4.4.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "NLog": { + "type": "Transitive", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "Polly": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Composition": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", + "dependencies": { + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw==" + }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" + }, + "TestableIO.System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.TestingHelpers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "XP7tiKVtnOP+jXWsRqgc7WCV2tsoolVnxSNUJXwdWmHCpLMsVlZXRag7dMynnNEQQB9XEJx25RteqN5APCnjag==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "monai.deploy.informaticsgateway.api": { + "type": "Project", + "dependencies": { + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.common": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" + } + }, + "monai.deploy.informaticsgateway.configuration": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" + } + }, + "monai.deploy.informaticsgateway.database.api": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" + } + }, + "monai.deploy.informaticsgateway.plugins.remoteappexecution": { + "type": "Project", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "NLog": "[5.4.0, )", + "Polly": "[8.5.2, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/Plug-ins/RemoteAppExecution/Utilities.cs b/src/Plug-ins/RemoteAppExecution/Utilities.cs new file mode 100644 index 000000000..8beb9cc94 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/Utilities.cs @@ -0,0 +1,59 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text.RegularExpressions; +using FellowOakDicom; + +namespace Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution +{ + internal static class Utilities + { + private static DicomTag GetDicomTagByName(string tag) => DicomDictionary.Default[tag] ?? DicomDictionary.Default[Regex.Replace(tag, @"\s+", "", RegexOptions.None, TimeSpan.FromSeconds(1))]; + + public static DicomTag[] GetTagArrayFromStringArray(string values) + { + var names = values.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + return names.Select(n => GetDicomTagByName(n)).ToArray(); + } + + public static T? GetTagProxyValue(DicomTag tag) where T : class + { + // partial implementation for now see + // https://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html + // for full list + switch (tag.DictionaryEntry.ValueRepresentations[0].Code) + { + case "UI": + case "LO": + case "LT": + { + return DicomUIDGenerator.GenerateDerivedFromUUID().UID as T; + } + case "SH": + case "AE": + case "CS": + case "PN": + case "ST": + case "UT": + { + return "no Value" as T; + } + default: + return default; + } + } + } +} diff --git a/src/Plug-ins/RemoteAppExecution/appsettings.json b/src/Plug-ins/RemoteAppExecution/appsettings.json new file mode 100644 index 000000000..57c93015b --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "InformaticsGatewayDatabase": "Data Source=mig.db" + } +} \ No newline at end of file diff --git a/src/Plug-ins/RemoteAppExecution/packages.lock.json b/src/Plug-ins/RemoteAppExecution/packages.lock.json new file mode 100644 index 000000000..8aba402f0 --- /dev/null +++ b/src/Plug-ins/RemoteAppExecution/packages.lock.json @@ -0,0 +1,761 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "Microsoft.EntityFrameworkCore": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" + } + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Direct", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Direct", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Direct", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[2.30.0, )", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" + } + }, + "NLog": { + "type": "Direct", + "requested": "[5.4.0, )", + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" + }, + "Polly": { + "type": "Direct", + "requested": "[8.5.2, )", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } + }, + "Ardalis.GuardClauses": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AWSSDK.Core": { + "type": "Transitive", + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" + }, + "AWSSDK.SecurityToken": { + "type": "Transitive", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", + "dependencies": { + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" + } + }, + "CommunityToolkit.HighPerformance": { + "type": "Transitive", + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "fo-dicom": { + "type": "Transitive", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", + "System.Buffers": "4.5.1", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", + "System.Threading.Channels": "6.0.0" + } + }, + "HL7-dotnetcore": { + "type": "Transitive", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Macross.Json.Extensions": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.HashCode": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" + } + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "Monai.Deploy.Messaging": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Messaging.RabbitMQ": { + "type": "Transitive", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", + "dependencies": { + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" + } + }, + "Monai.Deploy.Storage": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Storage.S3Policy": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "MongoDB.Driver.Core": { + "type": "Transitive", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", + "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "MongoDB.Libmongocrypt": { + "type": "Transitive", + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", + "dependencies": { + "System.CodeDom": "4.4.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "Polly.Core": { + "type": "Transitive", + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" + }, + "RabbitMQ.Client": { + "type": "Transitive", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.6" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Composition": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", + "dependencies": { + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" + } + }, + "System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + }, + "System.Threading.Channels": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "qmeeYNROMsONF6ndEZcIQ+VxR4Q/TX/7uIVLJqtwIWL7dDWeh0l1UIqgo4wYyjG//5lUNhwkLDSFl+pAWO6oiA==" + }, + "TestableIO.System.IO.Abstractions": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" + }, + "TestableIO.System.IO.Abstractions.Wrappers": { + "type": "Transitive", + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "monai.deploy.informaticsgateway.api": { + "type": "Project", + "dependencies": { + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.common": { + "type": "Project", + "dependencies": { + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" + } + }, + "monai.deploy.informaticsgateway.configuration": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" + } + }, + "monai.deploy.informaticsgateway.database.api": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/src/Shared/Test/InstanceGenerator.cs b/src/Shared/Test/InstanceGenerator.cs old mode 100644 new mode 100755 index 2287c18d6..5b666ec12 --- a/src/Shared/Test/InstanceGenerator.cs +++ b/src/Shared/Test/InstanceGenerator.cs @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 MONAI Consortium + * Copyright 2021-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ * limitations under the License. */ -using System.IO; using System.IO.Abstractions; using FellowOakDicom; using FellowOakDicom.Network; @@ -29,7 +28,9 @@ public static DicomCStoreRequest GenerateDicomCStoreRequest() } public static DicomFile GenerateDicomFile( +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. string studyInstanceUid = null, + string seriesInstanceUid = null, string sopInstanceUid = null, IFileSystem fileSystem = null) @@ -65,9 +66,10 @@ public static byte[] GenerateDicomData( var dataset = GenerateDicomDataset(studyInstanceUid, seriesInstanceUid, ref sopInstanceUid); var dicomfile = new DicomFile(dataset); - using var ms = new MemoryStream(); + using var ms = new System.IO.MemoryStream(); dicomfile.Save(ms); return ms.ToArray(); } } } +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. diff --git a/src/Shared/Test/TestStorageInfo.cs b/src/Shared/Test/TestStorageInfo.cs index 76627a8ef..a5dbc74b4 100644 --- a/src/Shared/Test/TestStorageInfo.cs +++ b/src/Shared/Test/TestStorageInfo.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,13 @@ */ using Monai.Deploy.InformaticsGateway.Api.Storage; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.SharedTest; internal record TestStorageInfo : FileStorageMetadata { - public TestStorageInfo(string correlationsId, string identifier, string filePath, string fileExtension) + public TestStorageInfo(string correlationsId, string identifier, string filePath, string fileExtension, DataOrigin dataOrigin) : base(correlationsId, identifier) { File = new StorageObjectMetadata(fileExtension) @@ -28,9 +29,17 @@ public TestStorageInfo(string correlationsId, string identifier, string filePath UploadPath = filePath, TemporaryPath = filePath }; + DataOrigin.Source = dataOrigin.Source; + DataOrigin.Destination = dataOrigin.Destination; + DataOrigin.DataService = dataOrigin.DataService; } public override string DataTypeDirectoryName => "dir"; public override StorageObjectMetadata File { get; set; } -} + + public void SetUploaded() + { + File.SetUploaded("test"); + } +} \ No newline at end of file diff --git a/src/Shared/Test/VerifyLogExtension.cs b/src/Shared/Test/VerifyLogExtension.cs old mode 100644 new mode 100755 index dc6e3781e..47577a908 --- a/src/Shared/Test/VerifyLogExtension.cs +++ b/src/Shared/Test/VerifyLogExtension.cs @@ -14,7 +14,9 @@ * limitations under the License. */ +#pragma warning disable IDE0005 // Using directive is unnecessary. using System; +#pragma warning restore IDE0005 // Using directive is unnecessary. using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; using Moq; @@ -28,6 +30,8 @@ public static Mock VerifyLoggingMessageEndsWith(this Mock logg { times ??= Times.Once(); +#pragma warning disable CS8602 // Dereference of a possibly null reference. +#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. Func state = (v, t) => v.ToString().EndsWith(expectedMessage); logger.Verify( @@ -130,8 +134,11 @@ public static Mock> VerifyLogging(this Mock> logger, Lo It.Is((v, t) => true), It.IsAny(), It.Is>((v, t) => true)), (Times)times); +#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types. return logger; } } + +#pragma warning restore CS8602 // Dereference of a possibly null reference. } diff --git a/src/nuget.txt b/src/nuget.txt new file mode 100644 index 000000000..30169738f --- /dev/null +++ b/src/nuget.txt @@ -0,0 +1,2028 @@ +Project 'Monai.Deploy.InformaticsGateway' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > DotNext.Threading 5.5.0 5.5.0 + > HL7-dotnetcore 2.39.1 2.39.1 + > Microsoft.EntityFrameworkCore.Design 8.0.14 8.0.14 + > Monai.Deploy.Messaging.RabbitMQ 2.0.4 2.0.4 + > Monai.Deploy.Security 1.0.1 1.0.1 + > Monai.Deploy.Storage.MinIO 1.0.2 1.0.2 + > NLog.Web.AspNetCore 5.4.0 5.4.0 + > Swashbuckle.AspNetCore 8.0.0 8.0.0 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AspNetCore.HealthChecks.MongoDb 8.1.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > DotNext 5.3.1 + > fo-dicom 5.2.1 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.AspNetCore.Authentication.JwtBearer 8.0.14 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Tools 8.0.14 + > Microsoft.Extensions.ApiDescription.Server 6.0.5 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.2 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 8.0.1 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.IdentityModel.Abstractions 7.1.2 + > Microsoft.IdentityModel.JsonWebTokens 7.1.2 + > Microsoft.IdentityModel.Logging 7.1.2 + > Microsoft.IdentityModel.Protocols 7.1.2 + > Microsoft.IdentityModel.Protocols.OpenIdConnect 7.1.2 + > Microsoft.IdentityModel.Tokens 7.1.2 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.OpenApi 1.6.23 + > Microsoft.Win32.Registry 5.0.0 + > Minio 6.0.2 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > NLog.Extensions.Logging 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > Swashbuckle.AspNetCore.Swagger 8.0.0 + > Swashbuckle.AspNetCore.SwaggerGen 8.0.0 + > Swashbuckle.AspNetCore.SwaggerUI 8.0.0 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.IdentityModel.Tokens.Jwt 7.1.2 + > System.IO.Abstractions 21.3.1 + > System.IO.Hashing 8.0.0 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reactive 6.0.0 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 8.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.Api' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > fo-dicom 5.2.1 5.2.1 + > HL7-dotnetcore 2.36.0 2.36.0 + > Macross.Json.Extensions 3.0.0 3.0.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 8.0.14 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + > Monai.Deploy.Messaging 2.0.4 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 2.0.3 + > Monai.Deploy.Storage 1.0.2 1.0.2 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Configuration' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Common' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Ardalis.GuardClauses 4.6.0 4.6.0 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + > System.IO.Abstractions 21.0.2 21.0.2 + + Transitive Package Resolved + > TestableIO.System.IO.Abstractions 21.0.2 + > TestableIO.System.IO.Abstractions.Wrappers 21.0.2 + +Project 'Monai.Deploy.InformaticsGateway.Database' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > AspNetCore.HealthChecks.MongoDb 8.1.0 8.1.0 + > Microsoft.EntityFrameworkCore.Tools 8.0.14 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.14 8.0.14 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 8.0.0 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.DicomWeb.Client' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > fo-dicom 5.2.1 5.2.1 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > CommunityToolkit.HighPerformance 8.3.2 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 6.0.0 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 6.0.0 + > Microsoft.Extensions.Options 6.0.0 + > Microsoft.Extensions.Primitives 6.0.0 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 6.0.0 + +Project 'Monai.Deploy.InformaticsGateway.Api.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Common.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > System.IO.Abstractions 21.0.2 21.0.2 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > Castle.Core 5.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Newtonsoft.Json 13.0.1 + > System.Diagnostics.EventLog 6.0.0 + > System.Reflection.Metadata 1.6.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Configuration.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AspNetCore.HealthChecks.MongoDb 8.1.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > DotNext 5.3.1 + > DotNext.Threading 5.5.0 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.39.1 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.AspNetCore.Authentication.JwtBearer 8.0.14 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Tools 8.0.14 + > Microsoft.Extensions.ApiDescription.Server 6.0.5 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.2 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 8.0.1 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.IdentityModel.Abstractions 7.1.2 + > Microsoft.IdentityModel.JsonWebTokens 7.1.2 + > Microsoft.IdentityModel.Logging 7.1.2 + > Microsoft.IdentityModel.Protocols 7.1.2 + > Microsoft.IdentityModel.Protocols.OpenIdConnect 7.1.2 + > Microsoft.IdentityModel.Tokens 7.1.2 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.OpenApi 1.6.23 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Microsoft.Win32.Registry 5.0.0 + > Minio 6.0.2 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.4 + > Monai.Deploy.Security 1.0.1 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.MinIO 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > NLog.Extensions.Logging 5.4.0 + > NLog.Web.AspNetCore 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > Swashbuckle.AspNetCore 8.0.0 + > Swashbuckle.AspNetCore.Swagger 8.0.0 + > Swashbuckle.AspNetCore.SwaggerGen 8.0.0 + > Swashbuckle.AspNetCore.SwaggerUI 8.0.0 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IdentityModel.Tokens.Jwt 7.1.2 + > System.IO.Abstractions 21.3.1 + > System.IO.Hashing 8.0.0 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reactive 6.0.0 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 8.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.DicomWeb.Client.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Ardalis.GuardClauses 4.6.0 4.6.0 + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 6.0.0 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 6.0.0 + > Microsoft.Extensions.Options 6.0.0 + > Microsoft.Extensions.Primitives 6.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Newtonsoft.Json 13.0.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 6.0.0 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.CLI' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Crayon 2.0.69 2.0.69 + > Docker.DotNet 3.125.15 3.125.15 + > Microsoft.Extensions.Http 8.0.0 8.0.0 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + > System.CommandLine.Hosting 0.4.0-alpha.22272.1 0.4.0-alpha.22272.1 + > System.CommandLine.Rendering 0.4.0-alpha.22272.1 0.4.0-alpha.22272.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CSharp 4.7.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.0 + > Microsoft.Extensions.Configuration.CommandLine 6.0.0 + > Microsoft.Extensions.Configuration.EnvironmentVariables 6.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 6.0.0 + > Microsoft.Extensions.Configuration.Json 6.0.0 + > Microsoft.Extensions.Configuration.UserSecrets 6.0.0 + > Microsoft.Extensions.DependencyInjection 8.0.0 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics 8.0.0 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 6.0.0 + > Microsoft.Extensions.FileSystemGlobbing 6.0.0 + > Microsoft.Extensions.Hosting 6.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 6.0.0 + > Microsoft.Extensions.Logging.Console 6.0.0 + > Microsoft.Extensions.Logging.Debug 6.0.0 + > Microsoft.Extensions.Logging.EventLog 6.0.0 + > Microsoft.Extensions.Logging.EventSource 6.0.0 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.CommandLine 2.0.0-beta4.22272.1 + > System.CommandLine.NamingConventionBinder 2.0.0-beta4.22272.1 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > System.Threading.Tasks.Extensions 4.5.4 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Client' has the following package references + [net8.0]: + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Client.Common' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Ardalis.GuardClauses 4.6.0 4.6.0 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + +Project 'Monai.Deploy.InformaticsGateway.Client.Common.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Ardalis.GuardClauses 4.6.0 4.6.0 + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Castle.Core 5.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Newtonsoft.Json 13.0.1 + > System.Diagnostics.EventLog 6.0.0 + > System.Reflection.Metadata 1.6.0 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Client.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AspNetCore.HealthChecks.MongoDb 8.1.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > DotNext 5.3.1 + > DotNext.Threading 5.5.0 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.39.1 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.AspNetCore.Authentication.JwtBearer 8.0.14 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Tools 8.0.14 + > Microsoft.Extensions.ApiDescription.Server 6.0.5 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.2 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 8.0.1 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.IdentityModel.Abstractions 7.1.2 + > Microsoft.IdentityModel.JsonWebTokens 7.1.2 + > Microsoft.IdentityModel.Logging 7.1.2 + > Microsoft.IdentityModel.Protocols 7.1.2 + > Microsoft.IdentityModel.Protocols.OpenIdConnect 7.1.2 + > Microsoft.IdentityModel.Tokens 7.1.2 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.OpenApi 1.6.23 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Microsoft.Win32.Registry 5.0.0 + > Minio 6.0.2 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.4 + > Monai.Deploy.Security 1.0.1 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.MinIO 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > NLog.Extensions.Logging 5.4.0 + > NLog.Web.AspNetCore 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > Swashbuckle.AspNetCore 8.0.0 + > Swashbuckle.AspNetCore.Swagger 8.0.0 + > Swashbuckle.AspNetCore.SwaggerGen 8.0.0 + > Swashbuckle.AspNetCore.SwaggerUI 8.0.0 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IdentityModel.Tokens.Jwt 7.1.2 + > System.IO.Abstractions 21.3.1 + > System.IO.Hashing 8.0.0 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reactive 6.0.0 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 8.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.CLI.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > System.CommandLine.Hosting 0.4.0-alpha.22272.1 0.4.0-alpha.22272.1 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > Crayon 2.0.69 + > Docker.DotNet 3.125.15 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.CSharp 4.7.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.0 + > Microsoft.Extensions.Configuration.CommandLine 6.0.0 + > Microsoft.Extensions.Configuration.EnvironmentVariables 6.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 6.0.0 + > Microsoft.Extensions.Configuration.Json 6.0.0 + > Microsoft.Extensions.Configuration.UserSecrets 6.0.0 + > Microsoft.Extensions.DependencyInjection 8.0.0 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics 8.0.0 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 6.0.0 + > Microsoft.Extensions.FileSystemGlobbing 6.0.0 + > Microsoft.Extensions.Hosting 6.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Http 8.0.0 + > Microsoft.Extensions.Logging 8.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 6.0.0 + > Microsoft.Extensions.Logging.Console 6.0.0 + > Microsoft.Extensions.Logging.Debug 6.0.0 + > Microsoft.Extensions.Logging.EventLog 6.0.0 + > Microsoft.Extensions.Logging.EventSource 6.0.0 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.CommandLine 2.0.0-beta4.22272.1 + > System.CommandLine.NamingConventionBinder 2.0.0-beta4.22272.1 + > System.CommandLine.Rendering 0.4.0-alpha.22272.1 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > System.Threading.Tasks.Extensions 4.5.4 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Integration.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > FluentAssertions 6.12.2 6.12.2 + > fo-dicom 5.2.1 5.2.1 + > HL7-dotnetcore 2.39.1 2.39.1 + > Microsoft.EntityFrameworkCore 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 8.0.14 + > Microsoft.Extensions.Configuration 8.0.0 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.2 8.0.2 + > Microsoft.Extensions.Configuration.EnvironmentVariables 8.0.0 8.0.0 + > Microsoft.Extensions.Configuration.Json 8.0.1 8.0.1 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Minio 6.0.2 6.0.2 + > Monai.Deploy.Messaging.RabbitMQ 2.0.4 2.0.4 + > Monai.Deploy.Storage.MinIO 1.0.2 1.0.2 + > Moq 4.20.70 4.20.70 + > Polly 8.5.2 8.5.2 + > RabbitMQ.Client 6.8.1 6.8.1 + > SpecFlow.Plus.LivingDocPlugin 3.9.57 3.9.57 + > SpecFlow.xUnit 3.9.74 3.9.74 + > System.Linq.Async 6.0.1 6.0.1 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AspNetCore.HealthChecks.MongoDb 8.1.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > BoDi 1.5.0 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > DotNext 5.3.1 + > DotNext.Threading 5.5.0 + > Gherkin 19.0.3 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.AspNetCore.Authentication.JwtBearer 8.0.14 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Tools 8.0.14 + > Microsoft.Extensions.ApiDescription.Server 6.0.5 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Logging.Configuration 8.0.1 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.IdentityModel.Abstractions 7.1.2 + > Microsoft.IdentityModel.JsonWebTokens 7.1.2 + > Microsoft.IdentityModel.Logging 7.1.2 + > Microsoft.IdentityModel.Protocols 7.1.2 + > Microsoft.IdentityModel.Protocols.OpenIdConnect 7.1.2 + > Microsoft.IdentityModel.Tokens 7.1.2 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.NETCore.Targets 1.1.0 + > Microsoft.OpenApi 1.6.23 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Security 1.0.1 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > NLog.Extensions.Logging 5.4.0 + > NLog.Web.AspNetCore 5.4.0 + > Polly.Core 8.5.2 + > runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.native.System 4.3.0 + > runtime.native.System.Net.Http 4.3.0 + > runtime.native.System.Security.Cryptography.Apple 4.3.0 + > runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple 4.3.0 + > runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl 4.3.2 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SpecFlow 3.9.74 + > SpecFlow.Internal.Json 1.0.8 + > SpecFlow.Tools.MsBuild.Generation 3.9.74 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > Swashbuckle.AspNetCore 8.0.0 + > Swashbuckle.AspNetCore.Swagger 8.0.0 + > Swashbuckle.AspNetCore.SwaggerGen 8.0.0 + > Swashbuckle.AspNetCore.SwaggerUI 8.0.0 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections 4.3.0 + > System.Collections.Concurrent 4.3.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.Configuration.ConfigurationManager 4.5.0 + > System.Diagnostics.Debug 4.3.0 + > System.Diagnostics.DiagnosticSource 4.3.0 + > System.Diagnostics.EventLog 6.0.0 + > System.Diagnostics.Tracing 4.3.0 + > System.Globalization 4.3.0 + > System.Globalization.Calendars 4.3.0 + > System.Globalization.Extensions 4.3.0 + > System.IdentityModel.Tokens.Jwt 7.1.2 + > System.IO 4.3.0 + > System.IO.Abstractions 21.3.1 + > System.IO.FileSystem 4.3.0 + > System.IO.FileSystem.Primitives 4.3.0 + > System.IO.Hashing 8.0.0 + > System.IO.Pipelines 6.0.3 + > System.Linq 4.3.0 + > System.Memory 4.5.5 + > System.Net.Http 4.3.4 + > System.Net.Primitives 4.3.0 + > System.Reactive 6.0.0 + > System.Reflection 4.3.0 + > System.Reflection.Metadata 6.0.1 + > System.Reflection.Primitives 4.3.0 + > System.Resources.ResourceManager 4.3.0 + > System.Runtime 4.3.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Runtime.Extensions 4.3.0 + > System.Runtime.Handles 4.3.0 + > System.Runtime.InteropServices 4.3.0 + > System.Runtime.Loader 4.3.0 + > System.Runtime.Numerics 4.3.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Cryptography.Algorithms 4.3.0 + > System.Security.Cryptography.Cng 4.3.0 + > System.Security.Cryptography.Csp 4.3.0 + > System.Security.Cryptography.Encoding 4.3.0 + > System.Security.Cryptography.OpenSsl 4.3.0 + > System.Security.Cryptography.Primitives 4.3.0 + > System.Security.Cryptography.ProtectedData 4.5.0 + > System.Security.Cryptography.X509Certificates 4.3.0 + > System.Security.Permissions 4.5.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding 4.3.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading 4.3.0 + > System.Threading.Channels 8.0.0 + > System.Threading.Tasks 4.3.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > Validation 2.4.18 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + > Xunit.SkippableFact 1.3.12 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.DicomWeb.Client.CLI' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > ConsoleAppFramework 4.2.4 4.2.4 + > Microsoft.NET.ILLink.Tasks (A) [8.0.14, ) 8.0.14 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.5.0 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.Extensions.Configuration 6.0.0 + > Microsoft.Extensions.Configuration.Abstractions 6.0.0 + > Microsoft.Extensions.Configuration.Binder 6.0.0 + > Microsoft.Extensions.Configuration.CommandLine 6.0.0 + > Microsoft.Extensions.Configuration.EnvironmentVariables 6.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 6.0.0 + > Microsoft.Extensions.Configuration.Json 6.0.0 + > Microsoft.Extensions.Configuration.UserSecrets 6.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 6.0.0 + > Microsoft.Extensions.FileProviders.Abstractions 6.0.0 + > Microsoft.Extensions.FileProviders.Physical 6.0.0 + > Microsoft.Extensions.FileSystemGlobbing 6.0.0 + > Microsoft.Extensions.Hosting 6.0.0 + > Microsoft.Extensions.Hosting.Abstractions 6.0.0 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 6.0.0 + > Microsoft.Extensions.Logging.Configuration 6.0.0 + > Microsoft.Extensions.Logging.Console 6.0.0 + > Microsoft.Extensions.Logging.Debug 6.0.0 + > Microsoft.Extensions.Logging.EventLog 6.0.0 + > Microsoft.Extensions.Logging.EventSource 6.0.0 + > Microsoft.Extensions.Options 6.0.0 + > Microsoft.Extensions.Options.ConfigurationExtensions 6.0.0 + > Microsoft.Extensions.Primitives 6.0.0 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 6.0.0 + +Project 'Monai.Deploy.InformaticsGateway.Database.EntityFramework' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Microsoft.EntityFrameworkCore 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 8.0.14 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 8.0.1 + > Polly 8.5.2 8.5.2 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Database.Api' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > NLog 5.4.0 5.4.0 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +Project 'Monai.Deploy.InformaticsGateway.Database.Api.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Database.EntityFramework.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.EntityFrameworkCore.InMemory 8.0.14 8.0.14 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + +Project 'Monai.Deploy.InformaticsGateway.Database.MongoDB' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > MongoDB.Driver 2.30.0 2.30.0 + > Polly 8.5.2 8.5.2 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.Database.MongoDB.Integration.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.2 6.0.2 + > FluentAssertions 6.12.2 6.12.2 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > System.Buffers 4.5.1 + > System.Configuration.ConfigurationManager 4.4.0 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Reflection.Metadata 1.6.0 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Cryptography.ProtectedData 4.4.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > Microsoft.EntityFrameworkCore 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 8.0.14 + > Microsoft.Extensions.Configuration 8.0.0 8.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 8.0.1 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 8.0.0 + > MongoDB.Driver 2.30.0 2.30.0 + > NLog 5.4.0 5.4.0 + > Polly 8.5.2 8.5.2 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.0 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution.Test' has the following package references + [net8.0]: + Top-level Package Requested Resolved + > coverlet.collector 6.0.4 6.0.4 + > Microsoft.EntityFrameworkCore.InMemory 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite 8.0.14 8.0.14 + > Microsoft.EntityFrameworkCore.Sqlite.Core 8.0.14 8.0.14 + > Microsoft.NET.Test.Sdk 17.13.0 17.13.0 + > Moq 4.20.70 4.20.70 + > System.IO.Abstractions.TestingHelpers 21.3.1 21.3.1 + > xRetry 1.9.0 1.9.0 + > xunit 2.8.1 2.8.1 + > xunit.runner.visualstudio 2.8.1 2.8.1 + + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > Castle.Core 5.1.1 + > CommunityToolkit.HighPerformance 8.3.2 + > DnsClient 1.6.1 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Humanizer.Core 2.14.1 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.CodeAnalysis.Analyzers 3.3.3 + > Microsoft.CodeAnalysis.Common 4.5.0 + > Microsoft.CodeAnalysis.CSharp 4.5.0 + > Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0 + > Microsoft.CodeAnalysis.Workspaces.Common 4.5.0 + > Microsoft.CodeCoverage 17.13.0 + > Microsoft.Data.Sqlite.Core 8.0.14 + > Microsoft.EntityFrameworkCore 8.0.14 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.EntityFrameworkCore.Analyzers 8.0.14 + > Microsoft.EntityFrameworkCore.Design 8.0.14 + > Microsoft.EntityFrameworkCore.Relational 8.0.14 + > Microsoft.Extensions.Caching.Abstractions 8.0.0 + > Microsoft.Extensions.Caching.Memory 8.0.1 + > Microsoft.Extensions.Configuration 8.0.0 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.Configuration.Binder 8.0.0 + > Microsoft.Extensions.Configuration.FileExtensions 8.0.1 + > Microsoft.Extensions.Configuration.Json 8.0.1 + > Microsoft.Extensions.DependencyInjection 8.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.DependencyModel 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.FileProviders.Physical 8.0.0 + > Microsoft.Extensions.FileSystemGlobbing 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 8.0.1 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Options.ConfigurationExtensions 8.0.0 + > Microsoft.Extensions.Primitives 8.0.0 + > Microsoft.NETCore.Platforms 5.0.0 + > Microsoft.TestPlatform.ObjectModel 17.13.0 + > Microsoft.TestPlatform.TestHost 17.13.0 + > Microsoft.Win32.Registry 5.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > MongoDB.Bson 2.30.0 + > MongoDB.Driver 2.30.0 + > MongoDB.Driver.Core 2.30.0 + > MongoDB.Libmongocrypt 1.12.0 + > Mono.TextTemplating 2.2.1 + > Newtonsoft.Json 13.0.3 + > NLog 5.4.0 + > Polly 8.5.2 + > Polly.Core 8.5.2 + > RabbitMQ.Client 6.8.1 + > SharpCompress 0.30.1 + > Snappier 1.0.0 + > SQLitePCLRaw.bundle_e_sqlite3 2.1.6 + > SQLitePCLRaw.core 2.1.6 + > SQLitePCLRaw.lib.e_sqlite3 2.1.6 + > SQLitePCLRaw.provider.e_sqlite3 2.1.6 + > System.Buffers 4.5.1 + > System.CodeDom 4.4.0 + > System.Collections.Immutable 6.0.0 + > System.Composition 6.0.0 + > System.Composition.AttributedModel 6.0.0 + > System.Composition.Convention 6.0.0 + > System.Composition.Hosting 6.0.0 + > System.Composition.Runtime 6.0.0 + > System.Composition.TypedParts 6.0.0 + > System.Diagnostics.EventLog 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.IO.Pipelines 6.0.3 + > System.Memory 4.5.5 + > System.Reflection.Metadata 6.0.1 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Security.AccessControl 5.0.0 + > System.Security.Principal.Windows 5.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.TestingHelpers 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + > xunit.abstractions 2.0.3 + > xunit.analyzers 1.14.0 + > xunit.assert 2.8.1 + > xunit.core 2.8.1 + > xunit.extensibility.core 2.8.1 + > xunit.extensibility.execution 2.8.1 + > ZstdSharp.Port 0.7.3 + +Project 'Monai.Deploy.InformaticsGateway.Test.PlugIns' has the following package references + [net8.0]: + Transitive Package Resolved + > Ardalis.GuardClauses 4.6.0 + > AWSSDK.Core 3.7.402.25 + > AWSSDK.SecurityToken 3.7.401.68 + > CommunityToolkit.HighPerformance 8.3.2 + > fo-dicom 5.2.1 + > HL7-dotnetcore 2.36.0 + > Macross.Json.Extensions 3.0.0 + > Microsoft.Bcl.AsyncInterfaces 8.0.0 + > Microsoft.Bcl.HashCode 1.1.1 + > Microsoft.EntityFrameworkCore.Abstractions 8.0.14 + > Microsoft.Extensions.Configuration.Abstractions 8.0.0 + > Microsoft.Extensions.DependencyInjection 6.0.1 + > Microsoft.Extensions.DependencyInjection.Abstractions 8.0.2 + > Microsoft.Extensions.Diagnostics.Abstractions 8.0.1 + > Microsoft.Extensions.Diagnostics.HealthChecks 8.0.14 + > Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions 8.0.14 + > Microsoft.Extensions.FileProviders.Abstractions 8.0.0 + > Microsoft.Extensions.Hosting.Abstractions 8.0.1 + > Microsoft.Extensions.Logging 6.0.0 + > Microsoft.Extensions.Logging.Abstractions 8.0.3 + > Microsoft.Extensions.Options 8.0.2 + > Microsoft.Extensions.Primitives 8.0.0 + > Monai.Deploy.Messaging 2.0.4 + > Monai.Deploy.Messaging.RabbitMQ 2.0.3 + > Monai.Deploy.Storage 1.0.2 + > Monai.Deploy.Storage.S3Policy 1.0.2 + > Newtonsoft.Json 13.0.3 + > Polly 8.4.0 + > Polly.Core 8.4.0 + > RabbitMQ.Client 6.8.1 + > System.Buffers 4.5.1 + > System.Diagnostics.DiagnosticSource 6.0.0 + > System.IO.Abstractions 21.3.1 + > System.Memory 4.5.5 + > System.Runtime.CompilerServices.Unsafe 6.0.0 + > System.Text.Encoding.CodePages 6.0.1 + > System.Text.Encodings.Web 8.0.0 + > System.Text.Json 8.0.5 + > System.Threading.Channels 7.0.0 + > TestableIO.System.IO.Abstractions 21.3.1 + > TestableIO.System.IO.Abstractions.Wrappers 21.3.1 + +(A) : Auto-referenced package. diff --git a/tests/Integration.Test/Common/Assertions.cs b/tests/Integration.Test/Common/Assertions.cs old mode 100644 new mode 100755 index 24119f0b0..074e5094c --- a/tests/Integration.Test/Common/Assertions.cs +++ b/tests/Integration.Test/Common/Assertions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,12 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Xml; -using Amazon.Runtime; using Ardalis.GuardClauses; using FellowOakDicom; using FellowOakDicom.Serialization; using Minio; +using Minio.DataModel.Args; +using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; using Monai.Deploy.Messaging.Events; @@ -34,7 +35,7 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.Common { - internal class Assertions + public class Assertions { private readonly Configurations _configurations; private readonly InformaticsGatewayConfiguration _options; @@ -49,10 +50,10 @@ public Assertions(Configurations configurations, InformaticsGatewayConfiguration _retryPolicy = Policy.Handle().WaitAndRetryAsync(retryCount: 5, sleepDurationProvider: _ => TimeSpan.FromMilliseconds(500)); } - internal async Task ShouldHaveUploadedDicomDataToMinio(IReadOnlyList messages, Dictionary fileHashes) + internal async Task ShouldHaveUploadedDicomDataToMinio(IReadOnlyList messages, Dictionary fileHashes, Action additionalChecks = null) { - Guard.Against.Null(messages); - Guard.Against.NullOrEmpty(fileHashes); + Guard.Against.Null(messages, nameof(messages)); + Guard.Against.NullOrEmpty(fileHashes, nameof(fileHashes)); var minioClient = GetMinioClient(); @@ -76,6 +77,12 @@ await _retryPolicy.ExecuteAsync(async () => memoryStream.Position = 0; var dicomFile = DicomFile.Open(memoryStream); dicomValidationKey = dicomFile.GenerateFileName(); + + if (additionalChecks is not null) + { + additionalChecks(dicomFile); + } + fileHashes.Should().ContainKey(dicomValidationKey).WhoseValue.Should().Be(dicomFile.CalculateHash()); }); await minioClient.GetObjectAsync(getObjectArgs); @@ -102,7 +109,7 @@ await _retryPolicy.ExecuteAsync(async () => internal async Task ShouldHaveUploadedFhirDataToMinio(IReadOnlyList messages, Dictionary fhirData) { - Guard.Against.Null(messages); + Guard.Against.Null(messages, nameof(messages)); var minioClient = GetMinioClient(); @@ -147,7 +154,7 @@ internal async Task ShouldHaveUploadedFhirDataToMinio(IReadOnlyList mes internal async Task ShouldHaveUploadedHl7ataToMinio(IReadOnlyList messages) { - Guard.Against.Null(messages); + Guard.Against.Null(messages, nameof(messages)); var minioClient = GetMinioClient(); @@ -189,10 +196,10 @@ internal async Task ShouldHaveUploadedHl7ataToMinio(IReadOnlyList messa } } - internal void ShouldHaveCorrectNumberOfWorkflowRequestMessages(DataProvider dataProvider, IReadOnlyList messages, int count) + internal void ShouldHaveCorrectNumberOfWorkflowRequestMessages(DataProvider dataProvider, DataService dataService, IReadOnlyList messages, int count) { - Guard.Against.Null(dataProvider); - Guard.Against.Null(messages); + Guard.Against.Null(dataProvider, nameof(dataProvider)); + Guard.Against.Null(messages, nameof(messages)); messages.Should().NotBeNullOrEmpty().And.HaveCount(count); foreach (var message in messages) @@ -200,19 +207,69 @@ internal void ShouldHaveCorrectNumberOfWorkflowRequestMessages(DataProvider data message.ApplicationId.Should().Be(MessageBrokerConfiguration.InformaticsGatewayApplicationId); var request = message.ConvertTo(); request.Should().NotBeNull(); - request.FileCount.Should().Be((dataProvider.DicomSpecs.NumberOfExpectedFiles(dataProvider.StudyGrouping))); + + if (dataProvider.ClientSendOverAssociations == 1 || messages.Count == 1) + { + request.FileCount.Should().Be((dataProvider.DicomSpecs.NumberOfExpectedFiles(dataProvider.StudyGrouping))); + } + else + { + request.FileCount.Should().Be(dataProvider.DicomSpecs.FileCount / dataProvider.ClientSendOverAssociations); + } if (dataProvider.Workflows is not null) { request.Workflows.Should().Equal(dataProvider.Workflows); } + request.DataTrigger.Should().NotBeNull(); + request.DataTrigger.DataService.Should().Be(dataService); + request.DataTrigger.Source.Should().Be(dataProvider.Source); + request.DataTrigger.Destination.Should().Be(dataProvider.Destination); + + foreach (var dataOrigin in request.DataOrigins) + { + dataOrigin.DataService.Should().Be(dataService); + dataOrigin.Source.Should().Be(dataProvider.Source); + dataOrigin.Destination.Should().Be(dataProvider.Destination); + } + } + } + + internal void ShouldHaveCorrectNumberOfWorkflowRequestMessagesForFhirRequest(DataProvider dataProvider, DataService dataService, IReadOnlyList messages, int count) + { + Guard.Against.Null(dataProvider, nameof(dataProvider)); + Guard.Against.Null(messages, nameof(messages)); + + messages.Should().NotBeNullOrEmpty().And.HaveCount(count); + foreach (var message in messages) + { + message.ApplicationId.Should().Be(MessageBrokerConfiguration.InformaticsGatewayApplicationId); + var request = message.ConvertTo(); + request.Should().NotBeNull(); + request.FileCount.Should().Be(1); + + if (dataProvider.Workflows is not null) + { + request.Workflows.Should().Equal(dataProvider.Workflows); + } + request.DataTrigger.Should().NotBeNull(); + request.DataTrigger.DataService.Should().Be(dataService); + request.DataTrigger.Source.Should().Be(dataProvider.Source); + request.DataTrigger.Destination.Should().Be(FileStorageMetadata.IpAddress()); + + foreach (var dataOrigin in request.DataOrigins) + { + dataOrigin.DataService.Should().Be(dataService); + dataOrigin.Source.Should().Be(dataProvider.Source); + dataOrigin.Destination.Should().Be(dataProvider.Destination); + } } } internal void ShouldHaveCorrectNumberOfWorkflowRequestMessagesAndAcrRequest(DataProvider dataProvider, IReadOnlyList messages, int count) { - Guard.Against.Null(dataProvider); - Guard.Against.Null(messages); + Guard.Against.Null(dataProvider, nameof(dataProvider)); + Guard.Against.Null(messages, nameof(messages)); messages.Should().NotBeNullOrEmpty().And.HaveCount(count); @@ -223,13 +280,22 @@ internal void ShouldHaveCorrectNumberOfWorkflowRequestMessagesAndAcrRequest(Data request.Should().NotBeNull(); request.FileCount.Should().Be(dataProvider.DicomSpecs.FileCount); request.Workflows.Should().Equal(dataProvider.AcrRequest.Application.Id); + request.DataTrigger.Should().NotBeNull(); + request.DataTrigger.DataService.Should().Be(DataService.ACR); + request.DataTrigger.Source.Should().Be(dataProvider.AcrRequest.TransactionId); + + foreach (var dataOrigin in request.DataOrigins) + { + dataOrigin.DataService.Should().Be(DataService.DicomWeb); + dataOrigin.Source.Should().Be(dataProvider.AcrRequest.TransactionId); + } } } internal void ShouldHaveCorrectNumberOfWorkflowRequestMessagesAndHl7Messages(Hl7Messages hL7Specs, IReadOnlyList messages, int count) { - Guard.Against.Null(hL7Specs); - Guard.Against.Null(messages); + Guard.Against.Null(hL7Specs, nameof(hL7Specs)); + Guard.Against.Null(messages, nameof(messages)); messages.Should().NotBeNullOrEmpty().And.HaveCount(count); @@ -242,7 +308,7 @@ internal void ShouldHaveCorrectNumberOfWorkflowRequestMessagesAndHl7Messages(Hl7 } } - private MinioClient GetMinioClient() => new MinioClient() + private MinioClient GetMinioClient() => (MinioClient)new MinioClient() .WithEndpoint(_options.Storage.Settings["endpoint"]) .WithCredentials(_options.Storage.Settings["accessKey"], _options.Storage.Settings["accessToken"]) .Build(); @@ -289,5 +355,57 @@ internal void ShoulddHaveCorrectNumberOfAckMessages(Dictionary r segment.Fields(9).Value.Should().Be("ACK"); } } + + internal async Task ShouldRestoreAllDicomMetaata(IReadOnlyList messages, Dictionary originalDicomFiles, params DicomTag[] dicomTags) + { + Guard.Against.Null(messages, nameof(messages)); + Guard.Against.NullOrEmpty(originalDicomFiles, nameof(originalDicomFiles)); + + var minioClient = GetMinioClient(); + + foreach (var message in messages) + { + var request = message.ConvertTo(); + foreach (var file in request.Payload) + { + await _retryPolicy.ExecuteAsync(async () => + { + var dicomValidationKey = string.Empty; + + _outputHelper.WriteLine($"Reading file from {request.Bucket} => {request.PayloadId}/{file.Path}."); + var getObjectArgs = new GetObjectArgs() + .WithBucket(request.Bucket) + .WithObject($"{request.PayloadId}/{file.Path}") + .WithCallbackStream((stream) => + { + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + memoryStream.Position = 0; + var dicomFile = DicomFile.Open(memoryStream); + dicomValidationKey = dicomFile.GenerateFileName(); + originalDicomFiles.Should().ContainKey(dicomValidationKey); + CompareDicomFiles(originalDicomFiles[dicomValidationKey], dicomFile, dicomTags); + }); + await minioClient.GetObjectAsync(getObjectArgs); + }); + } + } + } + + private void CompareDicomFiles(DicomFile left, DicomFile right, DicomTag[] dicomTags) + { + left.Should().NotBeNull(); + right.Should().NotBeNull(); + + foreach (var tag in dicomTags) + { + left.Dataset.GetString(tag).Should().Be(right.Dataset.GetString(tag)); + } + } + + public static void ShouldBeInMessageDictionary(Dictionary messages, HL7.Dotnetcore.Message message) + { + messages.Values.FirstOrDefault(m => m.HL7Message == message.HL7Message).Should().NotBeNull(); + } } } diff --git a/tests/Integration.Test/Common/DataProvider.cs b/tests/Integration.Test/Common/DataProvider.cs old mode 100644 new mode 100755 index cdab85f37..2748a16a2 --- a/tests/Integration.Test/Common/DataProvider.cs +++ b/tests/Integration.Test/Common/DataProvider.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,128 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.Common { + internal static class DicomRandomDataProvider + { + private static readonly Random Random = new(); + private static readonly string AlphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + private static readonly string Numeric = "0123456789"; + + public static void InjectRandomData(this DicomDataset dataset, DicomTag tag) + { + var data = string.Empty; + switch (tag.DictionaryEntry.ValueRepresentations[0].Code) + { + case "IS": + data = RandomString(Numeric, 12); + dataset.AddOrUpdate(tag, Convert.ToInt32(data)); + return; + + case "UI": + data = DicomUIDGenerator.GenerateDerivedFromUUID().UID; + break; + + case "LO": + case "LT": + data = RandomString(AlphaNumeric, 64); + break; + + case "AE": + data = RandomString(AlphaNumeric, 16); + break; + + case "CS": + data = RandomString(Numeric, 16); + break; + + case "FL": + var bufferFl = new byte[4]; + Random.NextBytes(bufferFl); + dataset.AddOrUpdate(tag, BitConverter.ToSingle(bufferFl, 0).ToString()); + return; + + case "FD": + var bufferFd = new byte[8]; + Random.NextBytes(bufferFd); + dataset.AddOrUpdate(tag, BitConverter.ToSingle(bufferFd, 0).ToString()); + return; + + case "OD": + var bufferOd = new byte[8]; + Random.NextBytes(bufferOd); + dataset.AddOrUpdate(tag, BitConverter.ToSingle(bufferOd, 0)); + return; + + case "OF": + var bufferOf = new byte[4]; + Random.NextBytes(bufferOf); + dataset.AddOrUpdate(tag, BitConverter.ToSingle(bufferOf, 0)); + return; + + case "PN": + data = RandomString(AlphaNumeric, 64); + break; + + case "DA": + data = "20000101"; + break; + + case "DT": + data = "20000101000000"; + break; + + case "TM": + data = "000000"; + break; + + case "SH": + data = RandomString(Numeric, 16); + break; + + case "DS": + case "SL": + case "UL": + data = RandomString(Numeric, 4); + break; + + case "SS": + case "US": + data = RandomString(Numeric, 2); + break; + + case "OB": + case "OW": + var bufferBytes = new byte[4]; + Random.NextBytes(bufferBytes); + dataset.AddOrUpdate(tag, bufferBytes); + break; + + case "ST": + case "UN": + case "UT": + data = RandomString(AlphaNumeric, 1024); + break; + + case "AS": + data = $"{RandomString(Numeric, 3).PadLeft(3, '0')}Y"; + break; + } + dataset.AddOrUpdate(tag, data); + } + + public static string RandomString(string characterSet, int maxLength) + { + var length = Random.Next(1, maxLength); + var output = new char[length]; + + for (int i = 0; i < length; i++) + { + output[i] = characterSet[Random.Next(characterSet.Length)]; + } + + return new string(output); + } + } + internal class DataProvider { private readonly Configurations _configurations; @@ -38,6 +160,11 @@ internal class DataProvider public DicomStatus DimseRsponse { get; internal set; } public string StudyGrouping { get; internal set; } public string[] Workflows { get; internal set; } = null; + public int ClientTimeout { get; internal set; } + public int ClientAssociationPulseTime { get; internal set; } = 0; + public int ClientSendOverAssociations { get; internal set; } = 1; + public string Source { get; internal set; } = string.Empty; + public string Destination { get; internal set; } = string.Empty; public DataProvider(Configurations configurations, ISpecFlowOutputHelper outputHelper) { @@ -50,7 +177,7 @@ public DataProvider(Configurations configurations, ISpecFlowOutputHelper outputH internal void GenerateDicomData(string modality, int studyCount, int? seriesPerStudy = null) { - Guard.Against.NullOrWhiteSpace(modality); + Guard.Against.NullOrWhiteSpace(modality, nameof(modality)); _outputHelper.WriteLine($"Generating {studyCount} {modality} study"); _configurations.StudySpecs.ContainsKey(modality).Should().BeTrue(); @@ -68,10 +195,21 @@ internal void GenerateDicomData(string modality, int studyCount, int? seriesPerS _outputHelper.WriteLine($"File specs: {DicomSpecs.StudyCount}, {DicomSpecs.SeriesPerStudyCount}, {DicomSpecs.InstancePerSeries}, {DicomSpecs.FileCount}"); } + internal void InjectRandomData(params DicomTag[] tags) + { + foreach (var dicomFile in DicomSpecs.Files.Values) + { + foreach (var tag in tags) + { + dicomFile.Dataset.InjectRandomData(tag); + } + } + } + internal void ReplaceGeneratedDicomDataWithHashes() { var dicomFileSize = new Dictionary(); - foreach (var dicomFile in DicomSpecs.Files) + foreach (var dicomFile in DicomSpecs.Files.Values) { var key = dicomFile.GenerateFileName(); dicomFileSize[key] = dicomFile.CalculateHash(); @@ -83,7 +221,7 @@ internal void ReplaceGeneratedDicomDataWithHashes() internal void GenerateAcrRequest(string requestType) { - Guard.Against.NullOrWhiteSpace(requestType); + Guard.Against.NullOrWhiteSpace(requestType, nameof(requestType)); var inferenceRequest = new InferenceRequest(); inferenceRequest.TransactionId = Guid.NewGuid().ToString(); @@ -102,12 +240,12 @@ internal void GenerateAcrRequest(string requestType) case "Patient": inferenceRequest.InputMetadata.Details.Type = InferenceRequestType.DicomPatientId; - inferenceRequest.InputMetadata.Details.PatientId = DicomSpecs.Files[0].Dataset.GetSingleValue(DicomTag.PatientID); + inferenceRequest.InputMetadata.Details.PatientId = DicomSpecs.Files.Values.First().Dataset.GetSingleValue(DicomTag.PatientID); break; case "AccessionNumber": inferenceRequest.InputMetadata.Details.Type = InferenceRequestType.AccessionNumber; - inferenceRequest.InputMetadata.Details.AccessionNumber = new List() { DicomSpecs.Files[0].Dataset.GetSingleValue(DicomTag.AccessionNumber) }; + inferenceRequest.InputMetadata.Details.AccessionNumber = new List() { DicomSpecs.Files.Values.First().Dataset.GetSingleValue(DicomTag.AccessionNumber) }; break; default: @@ -173,7 +311,8 @@ internal async Task GenerateHl7Messages(string version) var message = new HL7.Dotnetcore.Message(text); message.ParseMessage(); message.SetValue("MSH.10", file); - HL7Specs.Files[file] = message; + HL7Specs.Files[file] = new HL7.Dotnetcore.Message(message.SerializeMessage(false)); + HL7Specs.Files[file].ParseMessage(); } } } diff --git a/tests/Integration.Test/Common/DicomCEchoDataClient.cs b/tests/Integration.Test/Common/DicomCEchoDataClient.cs old mode 100644 new mode 100755 index 0a699f9cc..645224589 --- a/tests/Integration.Test/Common/DicomCEchoDataClient.cs +++ b/tests/Integration.Test/Common/DicomCEchoDataClient.cs @@ -36,10 +36,9 @@ public DicomCEchoDataClient(Configurations configurations, InformaticsGatewayCon _outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); } - public async Task SendAsync(DataProvider dataProvider, params object[] args) { - Guard.Against.NullOrEmpty(args); + Guard.Against.NullOrEmpty(args, nameof(args)); var callingAeTitle = args[0].ToString(); var host = args[1].ToString(); @@ -72,5 +71,6 @@ public async Task SendAsync(DataProvider dataProvider, params object[] args) dataProvider.DimseRsponse = DicomStatus.Cancel; } } + public Task SaveHl7Async(DataProvider dataProvider, params object[] args) => throw new NotImplementedException(); } } diff --git a/tests/Integration.Test/Common/DicomCStoreDataClient.cs b/tests/Integration.Test/Common/DicomCStoreDataClient.cs old mode 100644 new mode 100755 index e98566c88..bda321154 --- a/tests/Integration.Test/Common/DicomCStoreDataClient.cs +++ b/tests/Integration.Test/Common/DicomCStoreDataClient.cs @@ -41,24 +41,67 @@ public DicomCStoreDataClient(Configurations configurations, InformaticsGatewayCo _outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); } - public async Task SendAsync(DataProvider dataProvider, params object[] args) { - Guard.Against.NullOrEmpty(args); + Guard.Against.NullOrEmpty(args, nameof(args)); var callingAeTitle = args[0].ToString(); var host = args[1].ToString(); var port = (int)args[2]; var calledAeTitle = args[3].ToString(); - var timeout = (TimeSpan)args[4]; + var timeout = TimeSpan.FromSeconds(dataProvider.ClientTimeout); + var associations = dataProvider.ClientSendOverAssociations; + var pauseTime = TimeSpan.FromSeconds(dataProvider.ClientAssociationPulseTime); _outputHelper.WriteLine($"C-STORE: {callingAeTitle} => {host}:{port}@{calledAeTitle}"); var stopwatch = new Stopwatch(); stopwatch.Start(); - var dicomClient = DicomClientFactory.Create(host, port, false, callingAeTitle, calledAeTitle); - var countdownEvent = new CountdownEvent(dataProvider.DicomSpecs.Files.Count); + + var filesPerAssociations = dataProvider.DicomSpecs.Files.Count / associations; + var failureStatus = new List(); - foreach (var file in dataProvider.DicomSpecs.Files) + for (int i = 0; i < associations; i++) + { + var files = dataProvider.DicomSpecs.Files.Values.Skip(i * filesPerAssociations).Take(filesPerAssociations).ToList(); // .Take(20) + if (i + 1 == associations && dataProvider.DicomSpecs.Files.Count > (i + 1) * filesPerAssociations) + { + files.AddRange(dataProvider.DicomSpecs.Files.Values.Skip(i * filesPerAssociations)); + } + + try + { + await SendBatchAsync( + files, + callingAeTitle, + host, + port, + calledAeTitle, + timeout, + stopwatch, + failureStatus); + await Task.Delay(pauseTime); + } + catch (DicomAssociationRejectedException ex) + { + _outputHelper.WriteLine($"Association Rejected: {ex.Message}"); + dataProvider.DimseRsponse = DicomStatus.Cancel; + } + } + + stopwatch.Stop(); + lock (SyncRoot) + { + TotalTime += (int)stopwatch.Elapsed.TotalMilliseconds; + } + _outputHelper.WriteLine($"DICOMsend:{stopwatch.Elapsed.TotalSeconds}s"); + dataProvider.DimseRsponse = (failureStatus.Count == 0) ? DicomStatus.Success : failureStatus.First(); + } + + private async Task SendBatchAsync(List files, string callingAeTitle, string host, int port, string calledAeTitle, TimeSpan timeout, Stopwatch stopwatch, List failureStatus) + { + var dicomClient = DicomClientFactory.Create(host, port, false, callingAeTitle, calledAeTitle); + var countdownEvent = new CountdownEvent(files.Count); + foreach (var file in files) { var cStoreRequest = new DicomCStoreRequest(file); cStoreRequest.OnResponseReceived += (DicomCStoreRequest request, DicomCStoreResponse response) => @@ -69,24 +112,9 @@ public async Task SendAsync(DataProvider dataProvider, params object[] args) await dicomClient.AddRequestAsync(cStoreRequest); } - try - { - await dicomClient.SendAsync(); - countdownEvent.Wait(timeout); - stopwatch.Stop(); - lock (SyncRoot) - { - TotalTime += (int)stopwatch.Elapsed.TotalMilliseconds; - } - _outputHelper.WriteLine($"DICOMsend:{stopwatch.Elapsed.TotalSeconds}s"); - } - catch (DicomAssociationRejectedException ex) - { - _outputHelper.WriteLine($"Association Rejected: {ex.Message}"); - dataProvider.DimseRsponse = DicomStatus.Cancel; - } - - dataProvider.DimseRsponse = (failureStatus.Count == 0) ? DicomStatus.Success : failureStatus.First(); + await dicomClient.SendAsync(); + countdownEvent.Wait(timeout); } + public Task SaveHl7Async(DataProvider dataProvider, params object[] args) => throw new NotImplementedException(); } } diff --git a/tests/Integration.Test/Common/DicomDataSpecs.cs b/tests/Integration.Test/Common/DicomDataSpecs.cs index 9fa614187..b5db556e6 100644 --- a/tests/Integration.Test/Common/DicomDataSpecs.cs +++ b/tests/Integration.Test/Common/DicomDataSpecs.cs @@ -25,7 +25,7 @@ internal class DicomDataSpecs public int SeriesPerStudyCount { get; set; } public int InstancePerSeries { get; set; } public int FileCount { get; set; } - public List Files { get; set; } + public Dictionary Files { get; set; } = new Dictionary(); public Dictionary FileHashes { get; set; } = new Dictionary(); public int NumberOfExpectedRequests(string grouping) => grouping switch diff --git a/tests/Integration.Test/Common/DicomScp.cs b/tests/Integration.Test/Common/DicomScp.cs index edee5747e..ea56624f3 100644 --- a/tests/Integration.Test/Common/DicomScp.cs +++ b/tests/Integration.Test/Common/DicomScp.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ using System.Text; using FellowOakDicom; -using FellowOakDicom.Log; using FellowOakDicom.Network; +using Microsoft.Extensions.Logging; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; using TechTalk.SpecFlow.Infrastructure; namespace Monai.Deploy.InformaticsGateway.Integration.Test.Common { - public class DicomScp : IDisposable { public readonly string FeatureScpAeTitle = "TEST-SCP"; @@ -31,8 +31,12 @@ public class DicomScp : IDisposable private readonly IDicomServer _server; private bool _disposedValue; - public Dictionary Instances { get; set; } = new Dictionary(); + public Dictionary> Instances { get; set; } = new Dictionary>(); + public Dictionary DicomFiles { get; set; } = new Dictionary(); public ISpecFlowOutputHelper OutputHelper { get; set; } + + public bool ClearFilesAndUseHashes { get; set; } = true; + public DicomScp(ISpecFlowOutputHelper outputHelper) { OutputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); @@ -53,7 +57,6 @@ protected virtual void Dispose(bool disposing) } } - public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method @@ -62,19 +65,17 @@ public void Dispose() } } - - internal class CStoreScp : DicomService, IDicomServiceProvider, IDicomCStoreProvider { private static readonly object SyncLock = new object(); internal static readonly string PayloadsRoot = "./payloads"; - public CStoreScp(INetworkStream stream, Encoding fallbackEncoding, ILogger log, DicomServiceDependencies dicomServiceDependencies) - : base(stream, fallbackEncoding, log, dicomServiceDependencies) + public CStoreScp(INetworkStream stream, Encoding fallbackEncoding, ILogger logger, DicomServiceDependencies dicomServiceDependencies) + : base(stream, fallbackEncoding, logger, dicomServiceDependencies) { } - public void OnConnectionClosed(Exception exception) + public void OnConnectionClosedAsync(Exception exception) { if (exception is not null) { @@ -98,7 +99,17 @@ public Task OnCStoreRequestAsync(DicomCStoreRequest request var key = request.File.GenerateFileName(); lock (SyncLock) { - data.Instances.Add(key, request.File.CalculateHash()); + if (data.ClearFilesAndUseHashes) + { + var values = new List(); + data.Instances.Add(key, values); + values.Add(request.File.CalculateHash()); + values.Add(request.File.Dataset.GetSingleValueOrDefault(TestOutputDataPlugInModifyDicomFile.ExpectedTag, string.Empty)); + } + else + { + data.DicomFiles.Add(key, request.File); + } } data.OutputHelper.WriteLine("Instance received {0}", key); @@ -139,5 +150,8 @@ public Task OnReceiveAssociationRequestAsync(DicomAssociation association) return SendAssociationAcceptAsync(association); } + + public void OnConnectionClosed(Exception exception) + { } } } diff --git a/tests/Integration.Test/Common/DicomWebDataSink.cs b/tests/Integration.Test/Common/DicomWebDataSink.cs old mode 100644 new mode 100755 index dc7f2e8f2..1daa84fc8 --- a/tests/Integration.Test/Common/DicomWebDataSink.cs +++ b/tests/Integration.Test/Common/DicomWebDataSink.cs @@ -49,8 +49,8 @@ public DicomWebDataClient(Configurations configurations, InformaticsGatewayConfi /// public async Task SendAsync(DataProvider dataProvider, params object[] args) { - Guard.Against.Null(dataProvider); - Guard.Against.Null(args); + Guard.Against.Null(dataProvider, nameof(dataProvider)); + Guard.Against.Null(args, nameof(args)); var dicomFileSpec = dataProvider.DicomSpecs; dicomFileSpec.Should().NotBeNull(); @@ -59,7 +59,7 @@ public async Task SendAsync(DataProvider dataProvider, params object[] args) var endpoint = args[0].ToString(); _outputHelper.WriteLine($"POSTing studies to {endpoint} with {dicomFileSpec.Files.Count} files..."); - var httpClient = HttpClientFactory.Create(); + var httpClient = new HttpClient(); var dicomWebClient = new DicomWebClient(httpClient, null); dicomWebClient.ConfigureServiceUris(new Uri(endpoint)); @@ -81,5 +81,6 @@ public async Task SendAsync(DataProvider dataProvider, params object[] args) stopwatch.Stop(); _outputHelper.WriteLine($"Time to upload to DICOMWeb={0}s...", stopwatch.Elapsed.TotalSeconds); } + public Task SaveHl7Async(DataProvider dataProvider, params object[] args) => throw new NotImplementedException(); } } diff --git a/tests/Integration.Test/Common/FhirDataSink.cs b/tests/Integration.Test/Common/FhirDataSink.cs old mode 100644 new mode 100755 index 1157cfcf2..4671e5e06 --- a/tests/Integration.Test/Common/FhirDataSink.cs +++ b/tests/Integration.Test/Common/FhirDataSink.cs @@ -38,8 +38,8 @@ public FhirDataClient(Configurations configurations, InformaticsGatewayConfigura public async Task SendAsync(DataProvider dataProvider, params object[] args) { - Guard.Against.Null(dataProvider); - var httpClient = HttpClientFactory.Create(); + Guard.Against.Null(dataProvider, nameof(dataProvider)); + var httpClient = new HttpClient(); httpClient.BaseAddress = new Uri($"{_configurations.InformaticsGatewayOptions.ApiEndpoint}/fhir/"); httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(dataProvider.FhirSpecs.MediaType)); httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", dataProvider.FhirSpecs.MediaType); @@ -59,5 +59,6 @@ public async Task SendAsync(DataProvider dataProvider, params object[] args) stopwatch.Stop(); _outputHelper.WriteLine($"Time to upload FHIR data={0}s...", stopwatch.Elapsed.TotalSeconds); } + public Task SaveHl7Async(DataProvider dataProvider, params object[] args) => throw new NotImplementedException(); } } diff --git a/tests/Integration.Test/Common/Hl7DataSink.cs b/tests/Integration.Test/Common/Hl7DataSink.cs old mode 100644 new mode 100755 index 162ad4673..65fc0000c --- a/tests/Integration.Test/Common/Hl7DataSink.cs +++ b/tests/Integration.Test/Common/Hl7DataSink.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ +using System.Diagnostics; using System.Net.Sockets; using System.Text; using Ardalis.GuardClauses; @@ -38,8 +39,8 @@ public Hl7DataClient(Configurations configurations, InformaticsGatewayConfigurat public async Task SendAsync(DataProvider dataProvider, params object[] args) { - Guard.Against.Null(dataProvider); - Guard.Against.NullOrEmpty(args); + Guard.Against.Null(dataProvider, nameof(dataProvider)); + Guard.Against.NullOrEmpty(args, nameof(args)); var batch = (bool)args[0]; @@ -95,6 +96,8 @@ private async Task SendOneAsync(DataProvider dataProvider, params object[] args) private async Task SendBatchAsync(DataProvider dataProvider, params object[] args) { + var stopwatch = new Stopwatch(); + stopwatch.Start(); var messages = new List(); foreach (var file in dataProvider.HL7Specs.Files.Keys) { @@ -134,6 +137,9 @@ private async Task SendBatchAsync(DataProvider dataProvider, params object[] arg } } while (true); tcpClient.Close(); + stopwatch.Stop(); + _outputHelper.WriteLine($"Took {stopwatch.Elapsed.TotalSeconds}s to send {messages.Count} messages."); } + public Task SaveHl7Async(DataProvider dataProvider, params object[] args) => throw new NotImplementedException(); } } diff --git a/tests/Integration.Test/Common/IDataClient.cs b/tests/Integration.Test/Common/IDataClient.cs old mode 100644 new mode 100755 index 6f8f5a580..07ff97069 --- a/tests/Integration.Test/Common/IDataClient.cs +++ b/tests/Integration.Test/Common/IDataClient.cs @@ -14,11 +14,12 @@ * limitations under the License. */ - namespace Monai.Deploy.InformaticsGateway.Integration.Test.Common { internal interface IDataClient { Task SendAsync(DataProvider dataProvider, params object[] args); + + Task SaveHl7Async(DataProvider dataProvider, params object[] args); } } diff --git a/tests/Integration.Test/Common/MinioDataSink.cs b/tests/Integration.Test/Common/MinioDataSink.cs old mode 100644 new mode 100755 index 7d90f2ebd..3d0c8a5c5 --- a/tests/Integration.Test/Common/MinioDataSink.cs +++ b/tests/Integration.Test/Common/MinioDataSink.cs @@ -15,7 +15,9 @@ */ using System.Diagnostics; +using System.Text; using Minio; +using Minio.DataModel.Args; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; using Polly; @@ -50,7 +52,7 @@ await _retryPolicy.ExecuteAsync(async () => _outputHelper.WriteLine($"Uploading {dataProvider.DicomSpecs.FileCount} files to MinIO..."); - foreach (var file in dataProvider.DicomSpecs.Files) + foreach (var file in dataProvider.DicomSpecs.Files.Values) { var filename = file.GenerateFileName(); var stream = new MemoryStream(); @@ -69,7 +71,39 @@ await _retryPolicy.ExecuteAsync(async () => }); } - private MinioClient CreateMinioClient() => new MinioClient() + public async Task SaveHl7Async(DataProvider dataProvider, params object[] args) + { + await _retryPolicy.ExecuteAsync(async () => + { + var minioClient = CreateMinioClient(); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + _outputHelper.WriteLine($"Uploading {dataProvider.HL7Specs.Files.Count} files to MinIO..."); + + foreach (var key in dataProvider.HL7Specs.Files.Keys) + { + var file = dataProvider.HL7Specs.Files[key]; + var filename = $"{args[0]}/{key.Replace(".txt", ".hl7")}"; + var byteArray = Encoding.ASCII.GetBytes(file.HL7Message); + var stream = new MemoryStream(byteArray); + + stream.Position = 0; + var puObjectArgs = new PutObjectArgs(); + puObjectArgs.WithBucket(_options.Storage.StorageServiceBucketName) + .WithObject(filename) + .WithStreamData(stream) + .WithObjectSize(stream.Length); + await minioClient.PutObjectAsync(puObjectArgs); + } + + stopwatch.Stop(); + _outputHelper.WriteLine($"Time to upload to Minio={0}s...", stopwatch.Elapsed.TotalSeconds); + }); + } + + private MinioClient CreateMinioClient() => (MinioClient)new MinioClient() .WithEndpoint(_options.Storage.Settings["endpoint"]) .WithCredentials(_options.Storage.Settings["accessKey"], _options.Storage.Settings["accessToken"]) .Build(); diff --git a/tests/Integration.Test/Common/RabbitConnectionFactory.cs b/tests/Integration.Test/Common/RabbitConnectionFactory.cs old mode 100644 new mode 100755 index ad357e62f..6da3afa35 --- a/tests/Integration.Test/Common/RabbitConnectionFactory.cs +++ b/tests/Integration.Test/Common/RabbitConnectionFactory.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,10 +68,12 @@ public static void DeleteAllQueues(InformaticsGatewayConfiguration configuration { DeleteQueue(configuration.Messaging, configuration.Messaging.Topics.WorkflowRequest); DeleteQueue(configuration.Messaging, configuration.Messaging.Topics.ExportComplete); + DeleteQueue(configuration.Messaging, configuration.Messaging.Topics.ArtifactRecieved); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ExportRequestPrefix}.{configuration.Dicom.Scu.AgentName}"); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ExportRequestPrefix}.{configuration.DicomWeb.AgentName}"); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.WorkflowRequest}-dead-letter"); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ExportComplete}-dead-letter"); + DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ArtifactRecieved}-dead-letter"); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ExportRequestPrefix}.{configuration.Dicom.Scu.AgentName}-dead-letter"); DeleteQueue(configuration.Messaging, $"{configuration.Messaging.Topics.ExportRequestPrefix}.{configuration.DicomWeb.AgentName}-dead-letter"); } @@ -80,10 +82,12 @@ public static void PurgeAllQueues(MessageBrokerConfiguration configuration) { PurgeQueue(configuration, configuration.Topics.WorkflowRequest); PurgeQueue(configuration, configuration.Topics.ExportComplete); - PurgeQueue(configuration, configuration.Topics.ExportRequestPrefix); // TODO + PurgeQueue(configuration, configuration.Topics.ExportRequestPrefix); + PurgeQueue(configuration, configuration.Topics.ArtifactRecieved); PurgeQueue(configuration, $"{configuration.Topics.WorkflowRequest}-dead-letter"); PurgeQueue(configuration, $"{configuration.Topics.ExportComplete}-dead-letter"); - PurgeQueue(configuration, $"{configuration.Topics.ExportRequestPrefix}-dead-letter"); // TODO + PurgeQueue(configuration, $"{configuration.Topics.ExportRequestPrefix}-dead-letter"); + PurgeQueue(configuration, $"{configuration.Topics.ArtifactRecieved}-dead-letter"); } } } diff --git a/tests/Integration.Test/Drivers/DicomInstanceGenerator.cs b/tests/Integration.Test/Drivers/DicomInstanceGenerator.cs old mode 100644 new mode 100755 index 727c19cf3..9ce35d3bf --- a/tests/Integration.Test/Drivers/DicomInstanceGenerator.cs +++ b/tests/Integration.Test/Drivers/DicomInstanceGenerator.cs @@ -64,7 +64,7 @@ public DicomInstanceGenerator GenerateNewSeries() return this; } - public DicomFile GenerateNewInstance(long size, string sopClassUid = "1.2.840.10008.5.1.4.1.1.11.1") + public DicomFile GenerateNewInstance(long size, string modality, string sopClassUid = "1.2.840.10008.5.1.4.1.1.11.1") { var dataset = new DicomDataset(); _baseDataset.CopyTo(dataset); @@ -80,6 +80,12 @@ public DicomFile GenerateNewInstance(long size, string sopClassUid = "1.2.840.10 .AddOrUpdate(DicomTag.HighBit, 7) .AddOrUpdate(DicomTag.SamplesPerPixel, 1); + + if (modality.Any(char.IsLower) is false) + { + dataset.AddOrUpdate(DicomTag.Modality, modality); + } + var frames = Math.Max(1, size / Rows / Columns); var pixelData = DicomPixelData.Create(dataset, true); for (var frame = 0; frame < frames; frame++) @@ -103,7 +109,7 @@ public DicomDataSpecs Generate(string patientId, int studiesPerPatient, int seri studySpec.InstanceMax.Should().BeGreaterThan(0); var instancesPerSeries = _random.Next(studySpec.InstanceMin, studySpec.InstanceMax); - var files = new List(); + var files = new Dictionary(); var studyInstanceUids = new List(); DicomFile dicomFile = null; @@ -119,8 +125,8 @@ public DicomDataSpecs Generate(string patientId, int studiesPerPatient, int seri for (var instance = 0; instance < instancesPerSeries; instance++) { var size = _random.NextLong(studySpec.SizeMinBytes, studySpec.SizeMaxBytes); - dicomFile = generator.GenerateNewInstance(size); - files.Add(dicomFile); + dicomFile = generator.GenerateNewInstance(size, modality); + files.Add(dicomFile.GenerateFileName(), dicomFile); } } _outputHelper.WriteLine("DICOM Instance: PID={0}, STUDY={1}", diff --git a/tests/Integration.Test/Drivers/EfDataProvider.cs b/tests/Integration.Test/Drivers/EfDataProvider.cs old mode 100644 new mode 100755 index 2a8be5757..63b0bbb15 --- a/tests/Integration.Test/Drivers/EfDataProvider.cs +++ b/tests/Integration.Test/Drivers/EfDataProvider.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ public EfDataProvider(ISpecFlowOutputHelper outputHelper, Configurations configu private string ConvertToFullPath(string connectionString) { - Guard.Against.NullOrWhiteSpace(connectionString); + Guard.Against.NullOrWhiteSpace(connectionString, nameof(connectionString)); string absolute = Path.GetFullPath("./"); return connectionString.Replace("=./", $"={absolute}"); @@ -61,7 +61,15 @@ public void ClearAllData() DumpAndClear("DestinationApplicationEntities", _dbContext.DestinationApplicationEntities.ToList()); DumpAndClear("SourceApplicationEntities", _dbContext.SourceApplicationEntities.ToList()); DumpAndClear("MonaiApplicationEntities", _dbContext.MonaiApplicationEntities.ToList()); - DumpAndClear("Payloads", _dbContext.Payloads.ToList()); + DumpAndClear("VirtualApplicationEntities", _dbContext.VirtualApplicationEntities.ToList()); + try + { + DumpAndClear("Payloads", _dbContext.Payloads.ToList()); + } + catch (Exception) + { + } + DumpAndClear("InferenceRequests", _dbContext.InferenceRequests.ToList()); DumpAndClear("StorageMetadataWrapperEntities", _dbContext.StorageMetadataWrapperEntities.ToList()); _dbContext.SaveChanges(); diff --git a/tests/Integration.Test/Drivers/IDatabaseDataProvider.cs b/tests/Integration.Test/Drivers/IDatabaseDataProvider.cs index 47c1a0618..e0944c960 100644 --- a/tests/Integration.Test/Drivers/IDatabaseDataProvider.cs +++ b/tests/Integration.Test/Drivers/IDatabaseDataProvider.cs @@ -19,6 +19,7 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.Hooks public interface IDatabaseDataProvider { void ClearAllData(); + Task InjectAcrRequest(); } } diff --git a/tests/Integration.Test/Drivers/MongoDBDataProvider.cs b/tests/Integration.Test/Drivers/MongoDBDataProvider.cs old mode 100644 new mode 100755 index 9ef02d425..cf1ac780c --- a/tests/Integration.Test/Drivers/MongoDBDataProvider.cs +++ b/tests/Integration.Test/Drivers/MongoDBDataProvider.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Api.Rest; using Monai.Deploy.InformaticsGateway.Api.Storage; using Monai.Deploy.InformaticsGateway.Database.Api; @@ -37,6 +38,7 @@ public class MongoDBDataProvider : IDatabaseDataProvider private readonly IMongoCollection _sourceApplicationEntityCollection; private readonly IMongoCollection _destinationApplicationEntityCollection; private readonly IMongoCollection _monaiApplicationEntityCollection; + private readonly IMongoCollection _virtualApplicationEntityCollection; public MongoDBDataProvider(ISpecFlowOutputHelper outputHelper, Configurations configuration, string connectionString, string databaseName) { @@ -63,6 +65,7 @@ public MongoDBDataProvider(ISpecFlowOutputHelper outputHelper, Configurations co _sourceApplicationEntityCollection = _database.GetCollection(nameof(SourceApplicationEntity)); _destinationApplicationEntityCollection = _database.GetCollection(nameof(DestinationApplicationEntity)); _monaiApplicationEntityCollection = _database.GetCollection(nameof(MonaiApplicationEntity)); + _virtualApplicationEntityCollection = _database.GetCollection(nameof(VirtualApplicationEntity)); } public void ClearAllData() @@ -74,6 +77,7 @@ public void ClearAllData() DumpClear(_sourceApplicationEntityCollection); DumpClear(_destinationApplicationEntityCollection); DumpClear(_monaiApplicationEntityCollection); + DumpClear(_virtualApplicationEntityCollection); _outputHelper.WriteLine("All data removed from the database."); } diff --git a/tests/Integration.Test/Drivers/RabbitMqConsumer.cs b/tests/Integration.Test/Drivers/RabbitMqConsumer.cs index c3955d2ce..70b34da43 100644 --- a/tests/Integration.Test/Drivers/RabbitMqConsumer.cs +++ b/tests/Integration.Test/Drivers/RabbitMqConsumer.cs @@ -16,6 +16,7 @@ */ using System.Collections.Concurrent; +using System.Diagnostics; using Monai.Deploy.Messaging.Messages; using Monai.Deploy.Messaging.RabbitMQ; using TechTalk.SpecFlow.Infrastructure; @@ -30,8 +31,8 @@ internal class RabbitMqConsumer : IDisposable private readonly ConcurrentBag _messages; private bool _disposedValue; - public IReadOnlyList Messages { get { return _messages.ToList(); } } - public CountdownEvent MessageWaitHandle { get; private set; } + public IReadOnlyList Messages + { get { return _messages.ToList(); } } public RabbitMqConsumer(RabbitMQMessageSubscriberService subscriberService, string queueName, ISpecFlowOutputHelper outputHelper) { @@ -44,7 +45,7 @@ public RabbitMqConsumer(RabbitMQMessageSubscriberService subscriberService, stri _outputHelper = outputHelper ?? throw new ArgumentNullException(nameof(outputHelper)); _messages = new ConcurrentBag(); - subscriberService.Subscribe( + subscriberService.SubscribeAsync( queueName, queueName, (eventArgs) => @@ -53,15 +54,14 @@ public RabbitMqConsumer(RabbitMQMessageSubscriberService subscriberService, stri _messages.Add(eventArgs.Message); subscriberService.Acknowledge(eventArgs.Message); _outputHelper.WriteLine($"{DateTime.UtcNow} - {queueName} message received with correlation ID={eventArgs.Message.CorrelationId}, delivery tag={eventArgs.Message.DeliveryTag}"); - MessageWaitHandle?.Signal(); + return Task.CompletedTask; }); } - public void SetupMessageHandle(int count) + public void ClearMessages() { - _outputHelper.WriteLine($"Expecting {count} {_queueName} messages from RabbitMQ"); + _outputHelper.WriteLine($"Clearing messages received from RabbitMQ"); _messages.Clear(); - MessageWaitHandle = new CountdownEvent(count); } protected virtual void Dispose(bool disposing) @@ -77,12 +77,24 @@ protected virtual void Dispose(bool disposing) } } - public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } + + internal async Task WaitforAsync(int messageCount, TimeSpan messageWaitTimeSpan) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + while (messageCount > _messages.Count && stopwatch.Elapsed < messageWaitTimeSpan) + { + await Task.Delay(100); + } + + return messageCount >= _messages.Count; + } } } diff --git a/tests/Integration.Test/Features/AcrApi.feature b/tests/Integration.Test/Features/AcrApi.feature index 9c0bbec5c..9ad899b79 100644 --- a/tests/Integration.Test/Features/AcrApi.feature +++ b/tests/Integration.Test/Features/AcrApi.feature @@ -28,7 +28,7 @@ Feature: ACR API Given a DICOM study on a remote DICOMweb service And an ACR API request to query & retrieve by When the ACR API request is sent - Then a workflow requests sent to the message broker + Then a single workflow request is sent to the message broker And a study is uploaded to the storage service Examples: diff --git a/tests/Integration.Test/Features/DicomDimseScp.feature b/tests/Integration.Test/Features/DicomDimseScp.feature old mode 100644 new mode 100755 index fcaea8823..102ca3619 --- a/tests/Integration.Test/Features/DicomDimseScp.feature +++ b/tests/Integration.Test/Features/DicomDimseScp.feature @@ -1,4 +1,4 @@ -# Copyright 2022 MONAI Consortium +# Copyright 2022-2023 MONAI Consortium # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,37 +31,69 @@ Feature: DICOM DIMSE SCP Services Scenario: Response to C-ECHO-RQ Given a called AE Title named 'C-ECHO-TEST' that groups by '0020,000D' for 5 seconds - When a C-ECHO-RQ is sent to 'C-ECHO-TEST' from 'TEST-RUNNER' with timeout of 30 seconds + And a DICOM client configured with 30 seconds timeout + When a C-ECHO-RQ is sent to 'C-ECHO-TEST' from 'TEST-RUNNER' Then a successful response should be received @messaging_workflow_request @messaging Scenario Outline: Respond to C-STORE-RQ and group data by Study Instance UID - Given a called AE Title named 'C-STORE-STUDY' that groups by '0020,000D' for 3 seconds + Given a called AE Title named '' that groups by '0020,000D' for seconds + And a DICOM client configured with 300 seconds timeout + And a DICOM client configured to send data over 1 associations and wait 0 between each association And studies - When a C-STORE-RQ is sent to 'Informatics Gateway' with AET 'C-STORE-STUDY' from 'TEST-RUNNER' with timeout of 300 seconds + When a C-STORE-RQ is sent to 'Informatics Gateway' with AET '' from 'TEST-RUNNER' Then a successful response should be received - And workflow requests sent to message broker - And studies are uploaded to storage service + And Artifact Recieved sent to ea message broker + And studies are uploaded to storage service with data input plugins Examples: - | modality | count | - | MR | 1 | - | CT | 1 | - | MG | 2 | - | US | 1 | + | modality | count | aet | timeout | + | MR | 1 | C-STORE-STUDY30 | 3 | + | CT | 1 | C-STORE-STUDY30 | 3 | + | MG | 2 | C-STORE-STUDY10 | 3 | + | US | 1 | C-STORE-STUDY10 | 3 | @messaging_workflow_request @messaging Scenario Outline: Respond to C-STORE-RQ and group data by Series Instance UID - Given a called AE Title named 'C-STORE-SERIES' that groups by '0020,000E' for 3 seconds + Given a called AE Title named '' that groups by '0020,000E' for seconds + And a DICOM client configured with 300 seconds timeout + And a DICOM client configured to send data over 1 associations and wait 0 between each association And studies with series per study - When a C-STORE-RQ is sent to 'Informatics Gateway' with AET 'C-STORE-SERIES' from 'TEST-RUNNER' with timeout of 300 seconds + When a C-STORE-RQ is sent to 'Informatics Gateway' with AET '' from 'TEST-RUNNER' Then a successful response should be received - And workflow requests sent to message broker + And Artifact Recieved sent to ea message broker And studies are uploaded to storage service Examples: - | modality | study_count | series_count | - | MR | 1 | 2 | - | CT | 1 | 2 | - | MG | 1 | 3 | - | US | 1 | 2 | + | modality | study_count | series_count | aet | timeout | + | MR | 1 | 2 | C-STORE-SER30 | 3 | + | CT | 1 | 2 | C-STORE-SER30 | 3 | + | MG | 1 | 3 | C-STORE-SER10 | 3 | + | US | 1 | 2 | C-STORE-SER10 | 3 | + + @messaging_workflow_request @messaging + Scenario Outline: Respond to C-STORE-RQ and group data by Study Instance UID over multiple associations + Given a called AE Title named 'C-STORE-MA' that groups by '0020,000D' for 5 seconds + And a DICOM client configured with 300 seconds timeout + And a DICOM client configured to send data over associations and wait between each association + And studies with series per study + When C-STORE-RQ are sent to 'Informatics Gateway' with AET 'C-STORE-MA' from 'TEST-RUNNER' + Then a successful response should be received + And Artifact Recieved sent to ea message broker + And studies are uploaded to storage service + + Examples: + | modality | study_count | series_count | seconds | workflow_requests | + | MG | 1 | 3 | 3 | 1 | + | MG | 1 | 3 | 6 | 3 | + + @messaging_workflow_request @messaging + Scenario Outline: Respond to C-STORE-RQ and group data by Study Instance UID from external App + Given a called AE Title named 'C-STORE-MA' that groups by '0020,000D' for 5 seconds from external app + And a DICOM client configured with 300 seconds timeout + And a DICOM client configured to send data over 1 associations and wait 3 between each association + And 1 MG studies with 3 series per study + When C-STORE-RQ are sent to 'Informatics Gateway' with AET 'C-STORE-MA' from 'TEST-RUNNER' + Then a successful response should be received + And 1 Artifact Recieved sent to ea message broker + And studies are uploaded to storage service diff --git a/tests/Integration.Test/Features/DicomWebStow.feature b/tests/Integration.Test/Features/DicomWebStow.feature old mode 100644 new mode 100755 index bde7ec334..0e2687954 --- a/tests/Integration.Test/Features/DicomWebStow.feature +++ b/tests/Integration.Test/Features/DicomWebStow.feature @@ -1,4 +1,4 @@ -# Copyright 2022 MONAI Consortium +# Copyright 2022-2023 MONAI Consortium # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,8 +24,8 @@ Feature: DICOMweb STOW-RS Service Scenario: Triggers a new workflow request via DICOMWeb STOW-RS Given studies with 'stow_none' grouping When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/' - Then 1 workflow requests sent to message broker - And studies are uploaded to storage service + Then 1 workflow requests received from receieved artifact message broker + And studies are uploaded to storage service with data input plugins Examples: | modality | count | | MR | 1 | @@ -35,20 +35,20 @@ Feature: DICOMweb STOW-RS Service Scenario: Triggers a new workflow with given study instance UID request via DICOMWeb STOW-RS Given studies with 'stow_study' grouping When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/' with StudyInstanceUid - Then 1 workflow requests sent to message broker - And studies are uploaded to storage service + Then 1 workflow requests received from receieved artifact message broker + And studies are uploaded to storage service with data input plugins Examples: | modality | count | - | CT | 2 | - | US | 1 | + | MR | 1 | + | MG | 2 | @messaging_workflow_request @messaging Scenario: Triggers a new workflow via DICOMWeb STOW-RS Given studies with 'stow_none' grouping And a workflow named 'MyWorkflow' When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/' - Then 1 workflow requests sent to message broker - And studies are uploaded to storage service + Then 1 workflow requests received from receieved artifact message broker + And studies are uploaded to storage service with data input plugins Examples: | modality | count | | MR | 2 | @@ -59,9 +59,35 @@ Feature: DICOMweb STOW-RS Service Given studies with 'stow_study' grouping And a workflow named 'MyWorkflow' When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/' with StudyInstanceUid - Then 1 workflow requests sent to message broker - And studies are uploaded to storage service + Then 1 workflow requests received from receieved artifact message broker + And studies are uploaded to storage service with data input plugins Examples: | modality | count | | MR | 2 | | US | 1 | + + @messaging_workflow_request @messaging + Scenario: Triggers a new workflow request via DICOMWeb STOW-RS with Virtual AET + Given a VirtualAE 'cool' + And studies with 'stow_none' grouping + When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/vae/cool/' without overriding workflows + Then 1 workflow requests received from message broker + And studies are uploaded to storage service with data input VAE plugin + Examples: + | modality | count | + | MR | 1 | + | MG | 2 | + + + @messaging_workflow_request @messaging + Scenario: Triggers a new workflow with given study instance UID request via DICOMWeb STOW-RSw ith Virtual AET + Given a VirtualAE 'awesome' + And studies with 'stow_study' grouping + And a workflow named 'MyWorkflow' + When the studies are uploaded to the DICOMWeb STOW-RS service at '/dicomweb/vae/awesome/' with StudyInstanceUid + Then 1 workflow requests received from message broker + And studies are uploaded to storage service with data input VAE plugin + Examples: + | modality | count | + | MR | 1 | + | MG | 2 | diff --git a/tests/Integration.Test/Features/ExternalApp.feature b/tests/Integration.Test/Features/ExternalApp.feature new file mode 100755 index 000000000..831f0c85d --- /dev/null +++ b/tests/Integration.Test/Features/ExternalApp.feature @@ -0,0 +1,27 @@ + +# Copyright 2023 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# @ignored + +Feature: External App Execution + +This feature tests the External App Execution for saving and +re-identifying data sent and received by the MIG respectively. + + @messaging_workflow_request @messaging + Scenario: End-to-end test of external App scp incomming + Given a externalApp study that is exported to the test host + When the externalApp study is received and sent back to Informatics Gateway with 1 message + Then ensure the original externalApp study and the received study are the same diff --git a/tests/Integration.Test/Features/HL7Export.feature b/tests/Integration.Test/Features/HL7Export.feature new file mode 100755 index 000000000..b40748c8e --- /dev/null +++ b/tests/Integration.Test/Features/HL7Export.feature @@ -0,0 +1,32 @@ + +# Copyright 2023 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# @ignored + +Feature: HL7 Export + +This feature tests the Export Hl7. + + @messaging_workflow_request @messaging + Scenario: End-to-end test of HL7 exporting + Given a HL7 message that is exported to the test host + When the HL7 Export message is received with 6 messages acked true + Then ensure that exportcomplete messages are sent with success + + + Scenario: End-to-end test of HL7 exporting with no Ack + Given a HL7 message that is exported to the test host + When the HL7 Export message is received with 6 messages acked false + Then ensure that exportcomplete messages are sent with failure diff --git a/tests/Integration.Test/Features/RemoteAppExecutionPlugIn.feature b/tests/Integration.Test/Features/RemoteAppExecutionPlugIn.feature new file mode 100755 index 000000000..469408574 --- /dev/null +++ b/tests/Integration.Test/Features/RemoteAppExecutionPlugIn.feature @@ -0,0 +1,34 @@ +# Copyright 2022 MONAI Consortium +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# @ignored + +Feature: Remote App Execution Plug-in + +This feature tests the Remote App Execution plug-ins for de-identifying and +re-identifying data sent and received by the MIG respectively. + + + @messaging_workflow_request @messaging + Scenario: End-to-end test of plug-ins + Given a study that is exported to the test host + When the study is received and sent back to Informatics Gateway with 1 messages + Then ensure the original study and the received study are the same + + @messaging_workflow_request @messaging + Scenario: End-to-end test of plug-ins with one failing + Given a study that is exported to the test host with a bad plugin + When the study is received and sent back to Informatics Gateway with 2 messages + Then ensure the original study and the received study are the same + diff --git a/tests/Integration.Test/Hooks/TestHooks.cs b/tests/Integration.Test/Hooks/TestHooks.cs old mode 100644 new mode 100755 index 050424914..df08fe2df --- a/tests/Integration.Test/Hooks/TestHooks.cs +++ b/tests/Integration.Test/Hooks/TestHooks.cs @@ -40,7 +40,9 @@ public sealed class TestHooks private static RabbitMQConnectionFactory s_rabbitMqConnectionFactory; private static RabbitMQMessagePublisherService s_rabbitMqPublisher; private static RabbitMqConsumer s_rabbitMqConsumer_WorkflowRequest; + private static RabbitMqConsumer s_rabbitMqConsumer_ArtifactRecieved; private static RabbitMqConsumer s_rabbitMqConsumer_ExportComplete; + private static RabbitMqConsumer s_rabbitMqConsumer_ExportHL7Complete; private static IDatabaseDataProvider s_database; private static DicomScp s_dicomServer; private static DataProvider s_dataProvider; @@ -80,9 +82,10 @@ public static void Init(ISpecFlowOutputHelper outputHelper) s_hl7Sink = new Hl7DataClient(Configurations.Instance, s_options.Value, outputHelper); s_echoscu = new DicomCEchoDataClient(Configurations.Instance, s_options.Value, outputHelper); s_storescu = new DicomCStoreDataClient(Configurations.Instance, s_options.Value, outputHelper); - s_informaticsGatewayClient = new InformaticsGatewayClient(HttpClientFactory.Create(), scope.ServiceProvider.GetRequiredService>()); + s_informaticsGatewayClient = new InformaticsGatewayClient(new HttpClient(), scope.ServiceProvider.GetRequiredService>()); s_informaticsGatewayClient.ConfigureServiceUris(new Uri(Configurations.Instance.InformaticsGatewayOptions.ApiEndpoint)); - + s_options.Value.Dicom.Scu.MaximumNumberOfAssociations = 1; + s_options.Value.DicomWeb.MaximumNumberOfConnection = 1; var serviceLocator = scope.ServiceProvider.GetRequiredService(); s_informaticsGatewayHost.Start(); @@ -128,6 +131,21 @@ private static void SetupRabbitMq(ISpecFlowOutputHelper outputHelper, IServiceSc s_rabbitMqConnectionFactory); s_rabbitMqConsumer_ExportComplete = new RabbitMqConsumer(rabbitMqSubscriber_ExportComplete, s_options.Value.Messaging.Topics.ExportComplete, outputHelper); + + var rabbitMqSubscriber_ExportHL7Complete = new RabbitMQMessageSubscriberService( + Options.Create(s_options.Value.Messaging), + scope.ServiceProvider.GetRequiredService>(), + s_rabbitMqConnectionFactory); + + s_rabbitMqConsumer_ExportHL7Complete = new RabbitMqConsumer(rabbitMqSubscriber_ExportComplete, s_options.Value.Messaging.Topics.ExportHl7Complete, outputHelper); + + var rabbitMqSubscriber_ArtifactRecieved = new RabbitMQMessageSubscriberService( + Options.Create(s_options.Value.Messaging), + scope.ServiceProvider.GetRequiredService>(), + s_rabbitMqConnectionFactory); + + s_rabbitMqConsumer_ArtifactRecieved = new RabbitMqConsumer(rabbitMqSubscriber_ArtifactRecieved, s_options.Value.Messaging.Topics.ArtifactRecieved, outputHelper); + } private static IDatabaseDataProvider GetDatabase(IServiceProvider serviceProvider, ISpecFlowOutputHelper outputHelper) @@ -146,7 +164,7 @@ private static IDatabaseDataProvider GetDatabase(IServiceProvider serviceProvide else if (dbType == DatabaseManager.DbType_MongoDb) { var connectionString = config.GetSection("ConnectionStrings:InformaticsGatewayDatabase").Value; - var databaseName = config.GetSection("ConnectionStrings:DatabaseName").Value; + var databaseName = config.GetSection("ConnectionStrings:DatabaseOptions:DatabaseName").Value; return new MongoDBDataProvider(outputHelper, Configurations.Instance, connectionString, databaseName); } @@ -163,6 +181,8 @@ public void SetUp(ScenarioContext scenarioContext, ISpecFlowOutputHelper outputH _objectContainer.RegisterInstanceAs(s_rabbitMqPublisher, "MessagingPublisher"); _objectContainer.RegisterInstanceAs(s_rabbitMqConsumer_WorkflowRequest, "WorkflowRequestSubscriber"); _objectContainer.RegisterInstanceAs(s_rabbitMqConsumer_ExportComplete, "ExportCompleteSubscriber"); + _objectContainer.RegisterInstanceAs(s_rabbitMqConsumer_ArtifactRecieved, "ArtifactRecievedSubscriber"); + _objectContainer.RegisterInstanceAs(s_rabbitMqConsumer_ExportHL7Complete, "ExportHL7CompleteSubscriber"); _objectContainer.RegisterInstanceAs(s_dataProvider, "DataProvider"); _objectContainer.RegisterInstanceAs(s_assertions, "Assertions"); _objectContainer.RegisterInstanceAs(s_storescu, "StoreSCU"); @@ -182,10 +202,11 @@ public static void Shtudown() s_rabbitMqConsumer_WorkflowRequest.Dispose(); s_rabbitMqConsumer_ExportComplete.Dispose(); + s_rabbitMqConsumer_ExportHL7Complete.Dispose(); + s_rabbitMqConsumer_ArtifactRecieved.Dispose(); s_rabbitMqConnectionFactory.Dispose(); } - [AfterTestRun(Order = 0)] [AfterScenario] public static void ClearTestData(ISpecFlowOutputHelper outputHelper) diff --git a/tests/Integration.Test/Monai.Deploy.InformaticsGateway.Integration.Test.csproj b/tests/Integration.Test/Monai.Deploy.InformaticsGateway.Integration.Test.csproj old mode 100644 new mode 100755 index d6dd8681a..d9b2632d1 --- a/tests/Integration.Test/Monai.Deploy.InformaticsGateway.Integration.Test.csproj +++ b/tests/Integration.Test/Monai.Deploy.InformaticsGateway.Integration.Test.csproj @@ -1,5 +1,5 @@ - - - net6.0 + net8.0 enable true true - - - - - - - - - - - - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + - - - @@ -66,12 +64,12 @@ + + - - Always @@ -86,15 +84,13 @@ Always - - + - study.json @@ -104,4 +100,4 @@ - + \ No newline at end of file diff --git a/tests/Integration.Test/StepDefinitions/AcrApiStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/AcrApiStepDefinitions.cs index 56845731c..80c8aea65 100644 --- a/tests/Integration.Test/StepDefinitions/AcrApiStepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/AcrApiStepDefinitions.cs @@ -59,7 +59,7 @@ public async Task GivenADICOMStudySentToAETFromWithTimeoutOfSeconds() { var modality = "US"; _dataProvider.GenerateDicomData(modality, WorkflowStudyCount); - _receivedMessages.SetupMessageHandle(WorkflowStudyCount); + _receivedMessages.ClearMessages(); var storeScu = _objectContainer.Resolve("StoreSCU"); await storeScu.SendAsync(_dataProvider, "TEST-RUNNER", _configurations.OrthancOptions.Host, _configurations.OrthancOptions.DimsePort, "ORTHANC", TimeSpan.FromSeconds(300)); @@ -80,17 +80,16 @@ public async Task WhenTheACRAPIRequestIsSentTo() await _informaticsGatewayClient.Inference.NewInferenceRequest(_dataProvider.AcrRequest, CancellationToken.None); } - [Then(@"a workflow requests sent to the message broker")] - public void ThenAWorkflowRequestsSentToTheMessageBroker() + [Then(@"a single workflow request is sent to the message broker")] + public async Task ThenAWorkflowRequestsSentToTheMessageBroker() { - _receivedMessages.MessageWaitHandle.Wait(MessageWaitTimeSpan).Should().BeTrue(); + (await _receivedMessages.WaitforAsync(1, MessageWaitTimeSpan)).Should().BeTrue(); _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessagesAndAcrRequest(_dataProvider, _receivedMessages.Messages, WorkflowStudyCount); } [Then(@"a study is uploaded to the storage service")] public async Task ThenAStudyIsUploadedToTheStorageService() { - _receivedMessages.MessageWaitHandle.Wait(MessageWaitTimeSpan).Should().BeTrue(); _receivedMessages.Messages.Should().NotBeNullOrEmpty(); await _assertions.ShouldHaveUploadedDicomDataToMinio(_receivedMessages.Messages, _dataProvider.DicomSpecs.FileHashes); } diff --git a/tests/Integration.Test/StepDefinitions/DicomDimseScpServicesStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/DicomDimseScpServicesStepDefinitions.cs old mode 100644 new mode 100755 index 82b1d6bc4..7bad77fa8 --- a/tests/Integration.Test/StepDefinitions/DicomDimseScpServicesStepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/DicomDimseScpServicesStepDefinitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,14 @@ using BoDi; using FellowOakDicom.Network; using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Integration.Test.Common; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.Messaging.Common; +using Monai.Deploy.Messaging.Events; namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions { @@ -31,13 +34,16 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] public class DicomDimseScpServicesStepDefinitions { + internal static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromSeconds(120); internal static readonly string[] DummyWorkflows = new string[] { "WorkflowA", "WorkflowB" }; private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; private readonly ObjectContainer _objectContainer; private readonly Configurations _configuration; private readonly InformaticsGatewayClient _informaticsGatewayClient; private readonly RabbitMqConsumer _receivedMessages; + private readonly RabbitMqConsumer _receivedMessagesArtifactRecieved; private readonly DataProvider _dataProvider; + private readonly Assertions _assertions; public DicomDimseScpServicesStepDefinitions( ObjectContainer objectContainer, @@ -48,14 +54,16 @@ public DicomDimseScpServicesStepDefinitions( _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _receivedMessages = objectContainer.Resolve("WorkflowRequestSubscriber"); + _receivedMessagesArtifactRecieved = objectContainer.Resolve("ArtifactRecievedSubscriber"); _dataProvider = objectContainer.Resolve("DataProvider"); + _assertions = objectContainer.Resolve("Assertions"); _informaticsGatewayClient = objectContainer.Resolve("InformaticsGatewayClient"); } [Given(@"a calling AE Title '([^']*)'")] public async Task GivenACallingAETitle(string callingAeTitle) { - Guard.Against.NullOrWhiteSpace(callingAeTitle); + Guard.Against.NullOrWhiteSpace(callingAeTitle, nameof(callingAeTitle)); try { @@ -65,6 +73,7 @@ await _informaticsGatewayClient.DicomSources.Create(new SourceApplicationEntity AeTitle = callingAeTitle, HostIp = _configuration.InformaticsGatewayOptions.Host, }, CancellationToken.None); + _dataProvider.Source = callingAeTitle; } catch (ProblemException ex) { @@ -83,20 +92,25 @@ await _informaticsGatewayClient.DicomSources.Create(new SourceApplicationEntity [Given(@"(.*) (.*) studies with (.*) series per study")] public void GivenXStudiesWithYSeriesPerStudy(int studyCount, string modality, int seriesPerStudy) { - Guard.Against.NegativeOrZero(studyCount); - Guard.Against.NullOrWhiteSpace(modality); - Guard.Against.NegativeOrZero(seriesPerStudy); + Guard.Against.NegativeOrZero(studyCount, nameof(studyCount)); + Guard.Against.NullOrWhiteSpace(modality, nameof(modality)); + Guard.Against.NegativeOrZero(seriesPerStudy, nameof(seriesPerStudy)); _dataProvider.GenerateDicomData(modality, studyCount, seriesPerStudy); - _receivedMessages.SetupMessageHandle(_dataProvider.DicomSpecs.NumberOfExpectedRequests(_dataProvider.StudyGrouping)); + + _receivedMessages.ClearMessages(); + _receivedMessagesArtifactRecieved.ClearMessages(); } [Given(@"a called AE Title named '([^']*)' that groups by '([^']*)' for (.*) seconds")] public async Task GivenACalledAETitleNamedThatGroupsByForSeconds(string calledAeTitle, string grouping, uint groupingTimeout) { - Guard.Against.NullOrWhiteSpace(calledAeTitle); - Guard.Against.NullOrWhiteSpace(grouping); - Guard.Against.NegativeOrZero(groupingTimeout); + _receivedMessages.ClearMessages(); + _receivedMessagesArtifactRecieved.ClearMessages(); + + Guard.Against.NullOrWhiteSpace(calledAeTitle, nameof(calledAeTitle)); + Guard.Against.NullOrWhiteSpace(grouping, nameof(grouping)); + Guard.Against.NegativeOrZero(groupingTimeout, nameof(groupingTimeout)); _dataProvider.StudyGrouping = grouping; try @@ -107,9 +121,12 @@ await _informaticsGatewayClient.MonaiScpAeTitle.Create(new MonaiApplicationEntit Name = calledAeTitle, Grouping = grouping, Timeout = groupingTimeout, - Workflows = new List(DummyWorkflows) + Workflows = new List(DummyWorkflows), + PlugInAssemblies = new List() { typeof(Monai.Deploy.InformaticsGateway.Test.PlugIns.TestInputDataPlugInModifyDicomFile).AssemblyQualifiedName } }, CancellationToken.None); + _dataProvider.Workflows = DummyWorkflows; + _dataProvider.Destination = calledAeTitle; } catch (ProblemException ex) { @@ -124,13 +141,65 @@ await _informaticsGatewayClient.MonaiScpAeTitle.Create(new MonaiApplicationEntit } } } + [Given(@"a called AE Title named '([^']*)' that groups by '([^']*)' for (.*) seconds from external app")] + public async Task GivenACalledAETitleNamedThatGroupsByForSecondsFromExternalApp(string calledAeTitle, string grouping, uint groupingTimeout) + { + Guard.Against.NullOrWhiteSpace(calledAeTitle, nameof(calledAeTitle)); + Guard.Against.NullOrWhiteSpace(grouping, nameof(grouping)); + Guard.Against.NegativeOrZero(groupingTimeout, nameof(groupingTimeout)); - [When(@"a C-ECHO-RQ is sent to '([^']*)' from '([^']*)' with timeout of (.*) seconds")] - public async Task WhenAC_ECHO_RQIsSentToFromWithTimeoutOfSeconds(string calledAeTitle, string callingAeTitle, int clientTimeoutSeconds) + _dataProvider.StudyGrouping = grouping; + try + { + await _informaticsGatewayClient.MonaiScpAeTitle.Create(new MonaiApplicationEntity + { + AeTitle = calledAeTitle, + Name = calledAeTitle, + Grouping = grouping, + Timeout = groupingTimeout, + Workflows = new List(DummyWorkflows), + PlugInAssemblies = new List() { typeof(Monai.Deploy.InformaticsGateway.Test.PlugIns.TestInputDataPlugInModifyDicomFile).AssemblyQualifiedName }, + }, CancellationToken.None); + + _dataProvider.Workflows = DummyWorkflows; + _dataProvider.Destination = calledAeTitle; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.MonaiScpAeTitle.GetAeTitle(calledAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + } + + [Given(@"a DICOM client configured with (.*) seconds timeout")] + public void GivenADICOMClientConfiguredWithSecondsTimeout(int timeout) + { + Guard.Against.NegativeOrZero(timeout, nameof(timeout)); + _dataProvider.ClientTimeout = timeout; + } + + [Given(@"a DICOM client configured to send data over (.*) associations and wait (.*) between each association")] + public void GivenADICOMClientConfiguredToSendDataOverAssociationsAndWaitSecondsBetweenEachAssociation(int associations, int pulseTime) + { + Guard.Against.NegativeOrZero(associations, nameof(associations)); + Guard.Against.Negative(pulseTime, nameof(associations)); + + _dataProvider.ClientSendOverAssociations = associations; + _dataProvider.ClientAssociationPulseTime = pulseTime; + } + + [When(@"a C-ECHO-RQ is sent to '([^']*)' from '([^']*)'")] + public async Task WhenAC_ECHO_RQIsSentToFromWithTimeoutOfSeconds(string calledAeTitle, string callingAeTitle) { - Guard.Against.NullOrWhiteSpace(calledAeTitle); - Guard.Against.NullOrWhiteSpace(callingAeTitle); - Guard.Against.NegativeOrZero(clientTimeoutSeconds); + Guard.Against.NullOrWhiteSpace(calledAeTitle, nameof(calledAeTitle)); + Guard.Against.NullOrWhiteSpace(callingAeTitle, nameof(callingAeTitle)); var echoScu = _objectContainer.Resolve("EchoSCU"); await echoScu.SendAsync( @@ -139,7 +208,7 @@ await echoScu.SendAsync( _configuration.InformaticsGatewayOptions.Host, _informaticsGatewayConfiguration.Dicom.Scp.Port, calledAeTitle, - TimeSpan.FromSeconds(clientTimeoutSeconds)); + TimeSpan.FromSeconds(_dataProvider.ClientTimeout)); } [Then(@"a successful response should be received")] @@ -148,13 +217,13 @@ public void ThenASuccessfulResponseShouldBeReceived() _dataProvider.DimseRsponse.Should().Be(DicomStatus.Success); } - [When(@"a C-STORE-RQ is sent to '([^']*)' with AET '([^']*)' from '([^']*)' with timeout of (.*) seconds")] - public async Task WhenAC_STORE_RQIsSentToWithAETFromWithTimeoutOfSeconds(string application, string calledAeTitle, string callingAeTitle, int clientTimeoutSeconds) + [When(@"a C-STORE-RQ is sent to '([^']*)' with AET '([^']*)' from '([^']*)'")] + [When(@"C-STORE-RQ are sent to '([^']*)' with AET '([^']*)' from '([^']*)'")] + public async Task WhenAC_STORE_RQIsSentToWithAETFromWithTimeoutOfSeconds(string application, string calledAeTitle, string callingAeTitle) { - Guard.Against.NullOrWhiteSpace(application); - Guard.Against.NullOrWhiteSpace(calledAeTitle); - Guard.Against.NullOrWhiteSpace(callingAeTitle); - Guard.Against.NegativeOrZero(clientTimeoutSeconds); + Guard.Against.NullOrWhiteSpace(application, nameof(application)); + Guard.Against.NullOrWhiteSpace(calledAeTitle, nameof(calledAeTitle)); + Guard.Against.NullOrWhiteSpace(callingAeTitle, nameof(callingAeTitle)); var storeScu = _objectContainer.Resolve("StoreSCU"); @@ -168,10 +237,31 @@ await storeScu.SendAsync( callingAeTitle, host, port, - calledAeTitle, - TimeSpan.FromSeconds(clientTimeoutSeconds)); + calledAeTitle); _dataProvider.ReplaceGeneratedDicomDataWithHashes(); } + + [Then(@"(.*) workflow requests sent to message broker")] + public async Task ThenWorkflowRequestSentToMessageBrokerAsync(int workflowCount) + { + Guard.Against.NegativeOrZero(workflowCount, nameof(workflowCount)); + + (await _receivedMessages.WaitforAsync(workflowCount, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, Messaging.Events.DataService.DIMSE, _receivedMessages.Messages, workflowCount); + } + + [Then(@"(.*) Artifact Recieved sent to ea message broker")] + public async Task ThenArtifactRecievedSentToEaMessageBrokerExpect(int workflowCount) + { + Guard.Against.NegativeOrZero(workflowCount, nameof(workflowCount)); + + (await _receivedMessagesArtifactRecieved.WaitforAsync(workflowCount, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, Messaging.Events.DataService.DIMSE, _receivedMessagesArtifactRecieved.Messages, workflowCount); + + var firstMessage = _receivedMessagesArtifactRecieved.Messages.First().ConvertTo(); + + Assert.NotEqual(ArtifactType.Unset, firstMessage.Artifacts.First().Type); + } } } diff --git a/tests/Integration.Test/StepDefinitions/DicomWebStowServiceStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/DicomWebStowServiceStepDefinitions.cs old mode 100644 new mode 100755 index bf383b356..4ce427a54 --- a/tests/Integration.Test/StepDefinitions/DicomWebStowServiceStepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/DicomWebStowServiceStepDefinitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,17 @@ * limitations under the License. */ +using System.Net; using Ardalis.GuardClauses; using BoDi; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Client; +using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.DicomWeb.Client; using Monai.Deploy.InformaticsGateway.Integration.Test.Common; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions { @@ -27,11 +32,16 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] public class DicomWebStowServiceStepDefinitions { + internal static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromSeconds(130); + internal static readonly string[] DummyWorkflows = new string[] { "WorkflowA", "WorkflowB" }; private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; + private readonly InformaticsGatewayClient _informaticsGatewayClient; private readonly Configurations _configurations; private readonly RabbitMqConsumer _receivedMessages; + private readonly RabbitMqConsumer _artifactReceivedMessages; private readonly DataProvider _dataProvider; private readonly IDataClient _dataSink; + private readonly Assertions _assertions; public DicomWebStowServiceStepDefinitions(ObjectContainer objectContainer, Configurations configuration) { @@ -39,25 +49,65 @@ public DicomWebStowServiceStepDefinitions(ObjectContainer objectContainer, Confi _informaticsGatewayConfiguration = objectContainer.Resolve("InformaticsGatewayConfiguration"); _receivedMessages = objectContainer.Resolve("WorkflowRequestSubscriber"); + _artifactReceivedMessages = objectContainer.Resolve("ArtifactRecievedSubscriber"); _dataProvider = objectContainer.Resolve("DataProvider"); _dataSink = objectContainer.Resolve("DicomWebClient"); + _informaticsGatewayClient = objectContainer.Resolve("InformaticsGatewayClient"); + _assertions = objectContainer.Resolve("Assertions"); + + _dataProvider.Source = "::ffff:127.0.0.1"; + _dataProvider.Destination = "default"; + } + + [Given(@"a VirtualAE '(.*)'")] + public async Task GivenAVirtualAE(string virtualAe) + { + Guard.Against.NullOrWhiteSpace(virtualAe, nameof(virtualAe)); + + try + { + await _informaticsGatewayClient.VirtualAeTitle.Create(new VirtualApplicationEntity + { + Name = virtualAe, + VirtualAeTitle = virtualAe, + Workflows = new List(DummyWorkflows), + PlugInAssemblies = new List() { typeof(Monai.Deploy.InformaticsGateway.Test.PlugIns.TestInputDataPlugInVirtualAE).AssemblyQualifiedName } + }, CancellationToken.None); + + _dataProvider.Workflows = DummyWorkflows; + _dataProvider.Source = "::ffff:127.0.0.1"; + _dataProvider.Destination = virtualAe; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.VirtualAeTitle.GetAeTitle(virtualAe, CancellationToken.None); + } + else + { + throw; + } + } } [Given(@"(.*) (.*) studies with '(.*)' grouping")] public void GivenNStudies(int studyCount, string modality, string grouping) { - Guard.Against.NegativeOrZero(studyCount); - Guard.Against.NullOrWhiteSpace(modality); + Guard.Against.NegativeOrZero(studyCount, nameof(studyCount)); + Guard.Against.NullOrWhiteSpace(modality, nameof(modality)); _dataProvider.GenerateDicomData(modality, studyCount); _dataProvider.StudyGrouping = grouping; - _receivedMessages.SetupMessageHandle(_dataProvider.DicomSpecs.NumberOfExpectedRequests(grouping)); + _receivedMessages.ClearMessages(); + _artifactReceivedMessages.ClearMessages(); } [Given(@"a workflow named '(.*)'")] public void GivenNStudies(string workflowName) { - Guard.Against.NullOrWhiteSpace(workflowName); + Guard.Against.NullOrWhiteSpace(workflowName, nameof(workflowName)); _dataProvider.Workflows = new string[] { workflowName }; } @@ -65,11 +115,23 @@ public void GivenNStudies(string workflowName) [When(@"the studies are uploaded to the DICOMWeb STOW-RS service at '([^']*)'")] public async Task WhenStudiesAreUploadedToTheDicomWebStowRSServiceWithoutStudyInstanceUID(string endpoint) { - Guard.Against.NullOrWhiteSpace(endpoint); + Guard.Against.NullOrWhiteSpace(endpoint, nameof(endpoint)); await _dataSink.SendAsync(_dataProvider, $"{_configurations.InformaticsGatewayOptions.ApiEndpoint}{endpoint}", _dataProvider.Workflows, async (DicomWebClient dicomWebClient, DicomDataSpecs specs) => { - return await dicomWebClient.Stow.Store(specs.Files); + return await dicomWebClient.Stow.Store(specs.Files.Values); + }); + _dataProvider.ReplaceGeneratedDicomDataWithHashes(); + } + + [When(@"the studies are uploaded to the DICOMWeb STOW-RS service at '([^']*)' without overriding workflows")] + public async Task WhenStudiesAreUploadedToTheDicomWebStowRSServiceWithoutOverridingWorkflows(string endpoint) + { + Guard.Against.NullOrWhiteSpace(endpoint, nameof(endpoint)); + + await _dataSink.SendAsync(_dataProvider, $"{_configurations.InformaticsGatewayOptions.ApiEndpoint}{endpoint}", null, async (DicomWebClient dicomWebClient, DicomDataSpecs specs) => + { + return await dicomWebClient.Stow.Store(specs.Files.Values); }); _dataProvider.ReplaceGeneratedDicomDataWithHashes(); } @@ -77,14 +139,45 @@ await _dataSink.SendAsync(_dataProvider, $"{_configurations.InformaticsGatewayOp [When(@"the studies are uploaded to the DICOMWeb STOW-RS service at '([^']*)' with StudyInstanceUid")] public async Task WhenStudiesAreUploadedToTheDicomWebStowRSServiceWithStudyInstanceUID(string endpoint) { - Guard.Against.NullOrWhiteSpace(endpoint); + Guard.Against.NullOrWhiteSpace(endpoint, nameof(endpoint)); await _dataSink.SendAsync(_dataProvider, $"{_configurations.InformaticsGatewayOptions.ApiEndpoint}{endpoint}", _dataProvider.Workflows, async (DicomWebClient dicomWebClient, DicomDataSpecs specs) => { // Note: the MIG DICOMweb client ignores instances without matching StudyInstanceUID. - return await dicomWebClient.Stow.Store(specs.StudyInstanceUids.First(), specs.Files); + return await dicomWebClient.Stow.Store(specs.StudyInstanceUids.First(), specs.Files.Values); }); _dataProvider.ReplaceGeneratedDicomDataWithHashes(); } + + [Then(@"studies are uploaded to storage service with data input VAE plugin")] + public async Task ThenXXFilesUploadedToStorageServiceWithDataInputPlugIns() + { + await _assertions.ShouldHaveUploadedDicomDataToMinio( + _receivedMessages.Messages, + _dataProvider.DicomSpecs.FileHashes, + (dicomFile) => + { + dicomFile.Dataset.GetString(TestInputDataPlugInVirtualAE.ExpectedTag) + .Should().Be(TestInputDataPlugInVirtualAE.ExpectedValue); + }); + } + + [Then(@"(.*) workflow requests received from message broker")] + public async Task ThenWorkflowRequestSentToMessageBrokerAsync(int workflowCount) + { + Guard.Against.NegativeOrZero(workflowCount, nameof(workflowCount)); + + (await _receivedMessages.WaitforAsync(workflowCount, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, Messaging.Events.DataService.DicomWeb, _receivedMessages.Messages, workflowCount); + } + + [Then(@"(.*) workflow requests received from receieved artifact message broker")] + public async Task ThenWorkflowRequestsReceivedFromReceievedArtifactMessageBroker(int workflowCount) + { + Guard.Against.NegativeOrZero(workflowCount, nameof(workflowCount)); + + (await _artifactReceivedMessages.WaitforAsync(workflowCount, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, Messaging.Events.DataService.DicomWeb, _artifactReceivedMessages.Messages, workflowCount); + } } } diff --git a/tests/Integration.Test/StepDefinitions/ExportServicesStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/ExportServicesStepDefinitions.cs old mode 100644 new mode 100755 index b78d655e9..53cae4359 --- a/tests/Integration.Test/StepDefinitions/ExportServicesStepDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/ExportServicesStepDefinitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ using System.Net.Http.Headers; using Ardalis.GuardClauses; using BoDi; -using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; using Monai.Deploy.InformaticsGateway.Client; using Monai.Deploy.InformaticsGateway.Client.Common; using Monai.Deploy.InformaticsGateway.Configuration; @@ -26,6 +26,7 @@ using Monai.Deploy.InformaticsGateway.Integration.Test.Common; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; using Monai.Deploy.InformaticsGateway.Integration.Test.Hooks; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; using Monai.Deploy.Messaging.Events; using Monai.Deploy.Messaging.Messages; using Monai.Deploy.Messaging.RabbitMQ; @@ -36,7 +37,7 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] public class DicomDimseScuServicesStepDefinitions { - internal static readonly TimeSpan DicomScpWaitTimeSpan = TimeSpan.FromMinutes(3); + internal static readonly TimeSpan DicomScpWaitTimeSpan = TimeSpan.FromMinutes(20); private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; private readonly Configurations _configuration; private readonly DicomScp _dicomServer; @@ -103,8 +104,8 @@ public async Task GivenDICOMInstances() [Given(@"(.*) (.*) studies for export")] public async Task GivenDICOMInstances(int studyCount, string modality) { - Guard.Against.NegativeOrZero(studyCount); - Guard.Against.NullOrWhiteSpace(modality); + Guard.Against.NegativeOrZero(studyCount, nameof(studyCount)); + Guard.Against.NullOrWhiteSpace(modality, nameof(modality)); _dataProvider.GenerateDicomData(modality, studyCount); await _dataSink.SendAsync(_dataProvider); @@ -114,7 +115,7 @@ public async Task GivenDICOMInstances(int studyCount, string modality) [When(@"a export request is sent for '([^']*)'")] public void WhenAExportRequestIsReceivedDesignatedFor(string routingKey) { - Guard.Against.NullOrWhiteSpace(routingKey); + Guard.Against.NullOrWhiteSpace(routingKey, nameof(routingKey)); var exportRequestEvent = new ExportRequestEvent { @@ -126,32 +127,36 @@ public void WhenAExportRequestIsReceivedDesignatedFor(string routingKey) WorkflowInstanceId = Guid.NewGuid().ToString(), }; + exportRequestEvent.PluginAssemblies.Add(typeof(TestOutputDataPlugInModifyDicomFile).AssemblyQualifiedName); + var message = new JsonMessage( exportRequestEvent, MessageBrokerConfiguration.InformaticsGatewayApplicationId, exportRequestEvent.CorrelationId, string.Empty); - _receivedMessages.SetupMessageHandle(1); + _receivedMessages.ClearMessages(); _messagePublisher.Publish(routingKey, message.ToMessage()); } [Then(@"Informatics Gateway exports the studies to the DICOM SCP")] public async Task ThenExportTheInstancesToTheDicomScp() { - _receivedMessages.MessageWaitHandle.Wait(DicomScpWaitTimeSpan).Should().BeTrue(); + (await _receivedMessages.WaitforAsync(1, DicomScpWaitTimeSpan)).Should().BeTrue(); foreach (var key in _dataProvider.DicomSpecs.FileHashes.Keys) { (await Extensions.WaitUntil(() => _dicomServer.Instances.ContainsKey(key), DicomScpWaitTimeSpan)).Should().BeTrue("{0} should be received", key); - _dicomServer.Instances.Should().ContainKey(key).WhoseValue.Equals(_dataProvider.DicomSpecs.FileHashes[key]); + _dicomServer.Instances.Should().ContainKey(key).WhoseValue.Count.Equals(2); + _dicomServer.Instances.Should().ContainKey(key).WhoseValue[0].Equals(_dataProvider.DicomSpecs.FileHashes[key]); + _dicomServer.Instances.Should().ContainKey(key).WhoseValue[1].Equals(TestOutputDataPlugInModifyDicomFile.ExpectedValue); } } [Then(@"Informatics Gateway exports the studies to Orthanc")] public async Task ThenExportTheInstancesToOrthanc() { - _receivedMessages.MessageWaitHandle.Wait(DicomScpWaitTimeSpan).Should().BeTrue(); + (await _receivedMessages.WaitforAsync(1, DicomScpWaitTimeSpan)).Should().BeTrue(); var httpClient = new HttpClient(); var dicomWebClient = new DicomWebClient(httpClient, null); dicomWebClient.ConfigureServiceUris(new Uri(_configuration.OrthancOptions.DicomWebRoot)); @@ -159,20 +164,33 @@ public async Task ThenExportTheInstancesToOrthanc() var result = await Extensions.WaitUntilDataIsReady>(async () => { var actualHashes = new Dictionary(); + var exceptions = new List(); try { var instanceFound = 0; await foreach (var dicomFile in dicomWebClient.Wado.Retrieve(_dataProvider.DicomSpecs.StudyInstanceUids[0])) { - var key = dicomFile.GenerateFileName(); - var hash = dicomFile.CalculateHash(); - actualHashes.Add(key, hash); - ++instanceFound; + try + { + var key = dicomFile.GenerateFileName(); + var hash = dicomFile.CalculateHash(); + actualHashes.Add(key, hash); + dicomFile.Dataset.GetSingleValueOrDefault(TestOutputDataPlugInModifyDicomFile.ExpectedTag, string.Empty).Should().Be(TestOutputDataPlugInModifyDicomFile.ExpectedValue); + ++instanceFound; + } + catch (Exception ex) + { + exceptions.Add(ex); + } } } - catch + catch (Exception ex) + { + exceptions.Add(ex); + } + if (exceptions.Any()) { - // noop + throw new AggregateException(exceptions); } return actualHashes; }, (Dictionary expected) => diff --git a/tests/Integration.Test/StepDefinitions/ExteralAppStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/ExteralAppStepDefinitions.cs new file mode 100755 index 000000000..168155f11 --- /dev/null +++ b/tests/Integration.Test/StepDefinitions/ExteralAppStepDefinitions.cs @@ -0,0 +1,258 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Net; +using BoDi; +using FellowOakDicom; +using FellowOakDicom.Network; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Client; +using Monai.Deploy.InformaticsGateway.Client.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Integration.Test.Common; +using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Messaging.RabbitMQ; +using Polly; +using Polly.Timeout; + +namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions +{ + [Binding] + [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] + public class ExteralAppStepDefinitions + { + private static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromMinutes(3); + private static readonly TimeSpan DicomScpWaitTimeSpan = TimeSpan.FromMinutes(20); + private static readonly string MonaiAeTitle = "REMOTE-APPS"; + private static readonly string SourceAeTitle = "MIGTestHost"; + private static readonly DicomTag[] DicomTags = new[] { DicomTag.AccessionNumber, DicomTag.StudyDescription, DicomTag.SeriesDescription, DicomTag.PatientAddress, DicomTag.PatientAge, DicomTag.PatientName }; + private static readonly List DefaultDicomTags = new() { DicomTag.PatientID, DicomTag.StudyInstanceUID, DicomTag.SeriesInstanceUID, DicomTag.SOPInstanceUID }; + + private readonly ObjectContainer _objectContainer; + private readonly InformaticsGatewayClient _informaticsGatewayClient; + private readonly IDataClient _dataSinkMinio; + private readonly DicomScp _dicomServer; + private readonly Configurations _configuration; + private string _dicomDestination; + private readonly DataProvider _dataProvider; + private readonly RabbitMqConsumer _receivedExportCompletedMessages; + private readonly RabbitMqConsumer _receivedWorkflowRequestMessages; + private readonly RabbitMqConsumer _receivedArtifactRecievedMessages; + private readonly RabbitMQMessagePublisherService _messagePublisher; + private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; + private Dictionary _originalDicomFiles; + private ExternalAppRequestEvent _exportRequestEvent; + private readonly Assertions _assertions; + private readonly string _correlationId = Guid.NewGuid().ToString(); + private readonly string _exportTaskId = Guid.NewGuid().ToString(); + private readonly string _workflowInstanceId = Guid.NewGuid().ToString(); + + public ExteralAppStepDefinitions( + ObjectContainer objectContainer, + Configurations configuration) + { + _objectContainer = objectContainer ?? throw new ArgumentNullException(nameof(objectContainer)); + _informaticsGatewayClient = objectContainer.Resolve("InformaticsGatewayClient"); + _dataSinkMinio = objectContainer.Resolve("MinioClient"); + _dicomServer = objectContainer.Resolve("DicomScp"); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _dataProvider = objectContainer.Resolve("DataProvider"); + _receivedExportCompletedMessages = objectContainer.Resolve("ExportCompleteSubscriber"); + _receivedWorkflowRequestMessages = objectContainer.Resolve("WorkflowRequestSubscriber"); + _receivedArtifactRecievedMessages = objectContainer.Resolve("ArtifactRecievedSubscriber"); + _messagePublisher = objectContainer.Resolve("MessagingPublisher"); + _informaticsGatewayConfiguration = objectContainer.Resolve("InformaticsGatewayConfiguration"); + _assertions = objectContainer.Resolve("Assertions"); + + DefaultDicomTags.AddRange(DicomTags); + _dicomServer.ClearFilesAndUseHashes = false; //we need to store actual files to send the data back to MIG + } + + [Given(@"a externalApp study that is exported to the test host")] + public async Task GivenAExternalAppStudyThatIsExportedToTheTestHost() + { + DestinationApplicationEntity destination; + try + { + destination = await _informaticsGatewayClient.DicomDestinations.Create(new DestinationApplicationEntity + { + Name = _dicomServer.FeatureScpAeTitle, + AeTitle = _dicomServer.FeatureScpAeTitle, + HostIp = _configuration.InformaticsGatewayOptions.Host, + Port = _dicomServer.FeatureScpPort + }, CancellationToken.None); + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && ex.ProblemDetails.Detail.Contains("already exists")) + { + destination = await _informaticsGatewayClient.DicomDestinations.GetAeTitle(_dicomServer.FeatureScpAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + _dicomDestination = destination.Name; + + // Generate a study with multiple series + //_dataProvider.GenerateDicomData("MG", 1, 1); + _dataProvider.GenerateDicomData("CT", 1); + _dataProvider.InjectRandomData(DicomTags); + _originalDicomFiles = new Dictionary(_dataProvider.DicomSpecs.Files); + + await _dataSinkMinio.SendAsync(_dataProvider); + + // Emit a export request event + _exportRequestEvent = new ExternalAppRequestEvent + { + CorrelationId = _correlationId, + Targets = new List { new DataOrigin { Destination = destination.Name } }, + ExportTaskId = _exportTaskId, + Files = _dataProvider.DicomSpecs.Files.Keys.ToList(), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = _workflowInstanceId, + DestinationFolder = "ThisIs/My/Output/Folder", + }; + + //_exportRequestEvent.PluginAssemblies.Add(typeof(DicomDeidentifier).AssemblyQualifiedName); + + var message = new JsonMessage( + _exportRequestEvent, + MessageBrokerConfiguration.InformaticsGatewayApplicationId, + _exportRequestEvent.CorrelationId, + string.Empty); + + _receivedExportCompletedMessages.ClearMessages(); + _receivedArtifactRecievedMessages.ClearMessages(); + await _messagePublisher.Publish("md.externalapp.request", message.ToMessage()); + } + + [When(@"the externalApp study is received and sent back to Informatics Gateway with (.*) message")] + public async Task WhenTheExternalAppStudyIsReceivedAndSentBackToInformaticsGatewayWithMessage(int exportCount) + { + // setup DICOM Source + try + { + await _informaticsGatewayClient.DicomSources.Create(new SourceApplicationEntity + { + Name = SourceAeTitle, + AeTitle = SourceAeTitle, + HostIp = _configuration.InformaticsGatewayOptions.Host, + }, CancellationToken.None); + _dataProvider.Source = SourceAeTitle; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.DicomSources.GetAeTitle(SourceAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + + // setup MONAI Deploy AET + _dataProvider.StudyGrouping = "0020,000D"; + try + { + await _informaticsGatewayClient.MonaiScpAeTitle.Create(new MonaiApplicationEntity + { + AeTitle = MonaiAeTitle, + Name = MonaiAeTitle, + Grouping = _dataProvider.StudyGrouping, + Timeout = 3, + PlugInAssemblies = new List() + }, CancellationToken.None); + _dataProvider.Destination = MonaiAeTitle; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.MonaiScpAeTitle.GetAeTitle(MonaiAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + + var timeoutPolicy = Policy.TimeoutAsync(140, TimeoutStrategy.Pessimistic); + await timeoutPolicy + .ExecuteAsync( + async () => { await SendRequest(exportCount); } + ); + + // Clear workflow request messages + _receivedWorkflowRequestMessages.ClearMessages(); + _receivedArtifactRecievedMessages.ClearMessages(); + + _dataProvider.DimseRsponse.Should().Be(DicomStatus.Success); + + // Wait for workflow request events + (await _receivedArtifactRecievedMessages.WaitforAsync(1, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, DataService.DIMSE, _receivedArtifactRecievedMessages.Messages, 1); + } + + [Then(@"ensure the original externalApp study and the received study are the same")] + public async Task ThenEnsureTheOriginalExternalAppStudyAndTheReceivedStudyAreTheSame() + { + var workflowRequestEvent = _receivedArtifactRecievedMessages.Messages[0].ConvertTo(); + _exportRequestEvent.CorrelationId.Should().Be(_receivedArtifactRecievedMessages.Messages[0].CorrelationId); + _exportRequestEvent.CorrelationId.Should().Be(workflowRequestEvent.CorrelationId); + _exportRequestEvent.WorkflowInstanceId.Should().Be(workflowRequestEvent.WorkflowInstanceId); + _exportRequestEvent.ExportTaskId.Should().Be(workflowRequestEvent.TaskId); + await _assertions.ShouldRestoreAllDicomMetaata(_receivedArtifactRecievedMessages.Messages, _originalDicomFiles, DefaultDicomTags.ToArray()).ConfigureAwait(false); + } + + private async Task SendRequest(int exportCount = 1) + { + // Wait for export completed event + (await _receivedExportCompletedMessages.WaitforAsync(exportCount, DicomScpWaitTimeSpan)).Should().BeTrue(); + + foreach (var key in _dataProvider.DicomSpecs.FileHashes.Keys) + { + (await Extensions.WaitUntil(() => _dicomServer.Instances.ContainsKey(key), DicomScpWaitTimeSpan)).Should().BeTrue("{0} should be received", key); + } + + // Send data received back to MIG + var storeScu = _objectContainer.Resolve("StoreSCU"); + + var host = _configuration.InformaticsGatewayOptions.Host; + var port = _informaticsGatewayConfiguration.Dicom.Scp.ExternalAppPort; + + _dataProvider.Workflows = null; + _dataProvider.DicomSpecs.Files.Clear(); + _dataProvider.DicomSpecs.Files = new Dictionary(_dicomServer.DicomFiles); + _dataProvider.DicomSpecs.Files.Should().NotBeNull(); + + await storeScu.SendAsync( + _dataProvider, + SourceAeTitle, + host, + port, + MonaiAeTitle); + } + } +} diff --git a/tests/Integration.Test/StepDefinitions/FhirDefinitions.cs b/tests/Integration.Test/StepDefinitions/FhirDefinitions.cs old mode 100644 new mode 100755 index ab38d8c6b..98d72bc3b --- a/tests/Integration.Test/StepDefinitions/FhirDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/FhirDefinitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,9 +29,10 @@ public class FhirDefinitions internal enum FileFormat { Xml, Json }; - internal static readonly TimeSpan WaitTimeSpan = TimeSpan.FromMinutes(2); + internal static readonly TimeSpan WaitTimeSpan = TimeSpan.FromSeconds(120); private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; private readonly RabbitMqConsumer _receivedMessages; + private readonly RabbitMqConsumer _artifactReceivedMessages; private readonly DataProvider _dataProvider; private readonly Assertions _assertions; private readonly IDataClient _dataSink; @@ -45,19 +46,22 @@ public FhirDefinitions(ObjectContainer objectContainer) _informaticsGatewayConfiguration = objectContainer.Resolve("InformaticsGatewayConfiguration"); _receivedMessages = objectContainer.Resolve("WorkflowRequestSubscriber"); + _artifactReceivedMessages = objectContainer.Resolve("ArtifactRecievedSubscriber"); _dataProvider = objectContainer.Resolve("DataProvider"); _assertions = objectContainer.Resolve("Assertions"); _dataSink = objectContainer.Resolve("FhirClient"); + + _dataProvider.Source = "::ffff:127.0.0.1"; } [Given(@"FHIR message (.*) in (.*)")] public async Task GivenHl7MessagesInVersionX(string version, string format) { - Guard.Against.NullOrWhiteSpace(version); - Guard.Against.NullOrWhiteSpace(format); + Guard.Against.NullOrWhiteSpace(version, nameof(version)); + Guard.Against.NullOrWhiteSpace(format, nameof(format)); await _dataProvider.GenerateFhirMessages(version, format); - _receivedMessages.SetupMessageHandle(_dataProvider.FhirSpecs.Files.Count); + _receivedMessages.ClearMessages(); } [When(@"the FHIR messages are sent to Informatics Gateway")] @@ -67,9 +71,10 @@ public async Task WhenTheMessagesAreSentToInformaticsGateway() } [Then(@"workflow requests are sent to message broker")] - public void ThenWorkflowRequestAreSentToMessageBroker() + public async Task ThenWorkflowRequestAreSentToMessageBrokerAsync() { - _receivedMessages.MessageWaitHandle.Wait(WaitTimeSpan).Should().BeTrue(); + (await _receivedMessages.WaitforAsync(_dataProvider.FhirSpecs.Files.Count, WaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessagesForFhirRequest(_dataProvider, Messaging.Events.DataService.FHIR, _receivedMessages.Messages, _dataProvider.FhirSpecs.Files.Count); } [Then(@"FHIR resources are uploaded to storage service")] diff --git a/tests/Integration.Test/StepDefinitions/HealthLevel7Definitions.cs b/tests/Integration.Test/StepDefinitions/HealthLevel7Definitions.cs index 53436dfd5..9a8d94399 100644 --- a/tests/Integration.Test/StepDefinitions/HealthLevel7Definitions.cs +++ b/tests/Integration.Test/StepDefinitions/HealthLevel7Definitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,9 +47,9 @@ public HealthLevel7Definitions(ObjectContainer objectContainer) [Given(@"HL7 messages in version (.*)")] public async Task GivenHl7MessagesInVersionX(string version) { - Guard.Against.NullOrWhiteSpace(version); + Guard.Against.NullOrWhiteSpace(version, nameof(version)); await _dataProvider.GenerateHl7Messages(version); - _receivedMessages.SetupMessageHandle(1); + _receivedMessages.ClearMessages(); } [When(@"the message are sent to Informatics Gateway")] @@ -71,9 +71,9 @@ public void ThenAcknowledgementAreReceived() } [Then(@"a workflow requests sent to message broker")] - public void ThenAWorkflowRequestIsSentToMessageBroker() + public async Task ThenAWorkflowRequestIsSentToMessageBrokerAsync() { - _receivedMessages.MessageWaitHandle.Wait(WaitTimeSpan).Should().BeTrue(); + (await _receivedMessages.WaitforAsync(1, WaitTimeSpan)).Should().BeTrue(); } [Then(@"messages are uploaded to storage service")] diff --git a/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs b/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs new file mode 100755 index 000000000..43394a042 --- /dev/null +++ b/tests/Integration.Test/StepDefinitions/Hl7StepDefinitions.cs @@ -0,0 +1,236 @@ +/* + * Copyright 2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using BoDi; +using FellowOakDicom; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Client; +using Monai.Deploy.InformaticsGateway.Client.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Integration.Test.Common; +using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Messaging.RabbitMQ; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions +{ + [Binding] + [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] + + internal class Hl7StepDEfinitions + { + private static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromMinutes(3); + private static readonly DicomTag[] DicomTags = new[] { DicomTag.AccessionNumber, DicomTag.StudyDescription, DicomTag.SeriesDescription, DicomTag.PatientAddress, DicomTag.PatientAge, DicomTag.PatientName }; + private static readonly List DefaultDicomTags = new() { DicomTag.PatientID, DicomTag.StudyInstanceUID, DicomTag.SeriesInstanceUID, DicomTag.SOPInstanceUID }; + + private readonly ObjectContainer _objectContainer; + private readonly InformaticsGatewayClient _informaticsGatewayClient; + private readonly IDataClient _dataSinkMinio; + private readonly DicomScp _dicomServer; + private readonly Configurations _configuration; + private string _dicomDestination; + private readonly DataProvider _dataProvider; + private readonly RabbitMqConsumer _receivedExportHL7CompletedMessages; + private readonly RabbitMQMessagePublisherService _messagePublisher; + private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; + private Dictionary _originalHL7Files; + private ExportRequestEvent _exportRequestEvent; + private readonly Assertions _assertions; + private readonly string _correlationId = Guid.NewGuid().ToString(); + private readonly string _exportTaskId = Guid.NewGuid().ToString(); + private readonly string _workflowInstanceId = Guid.NewGuid().ToString(); + internal static readonly TimeSpan WaitTimeSpan = TimeSpan.FromSeconds(30); + private readonly string _hl7SendAddress = "127.0.0.1"; + private readonly int _hl7Port = 2574; + private JsonMessage _messageToSend; + + private readonly List _hl7Messages = new List(); + private TcpListener _tcpListener; + + public Hl7StepDEfinitions(ObjectContainer objectContainer, Configurations configuration) + { + _objectContainer = objectContainer ?? throw new ArgumentNullException(nameof(objectContainer)); + _informaticsGatewayClient = objectContainer.Resolve("InformaticsGatewayClient"); + _dataSinkMinio = objectContainer.Resolve("MinioClient"); + _dicomServer = objectContainer.Resolve("DicomScp"); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _dataProvider = objectContainer.Resolve("DataProvider"); + _receivedExportHL7CompletedMessages = objectContainer.Resolve("ExportHL7CompleteSubscriber"); + _messagePublisher = objectContainer.Resolve("MessagingPublisher"); + _informaticsGatewayConfiguration = objectContainer.Resolve("InformaticsGatewayConfiguration"); + _assertions = objectContainer.Resolve("Assertions"); + + DefaultDicomTags.AddRange(DicomTags); + _dicomServer.ClearFilesAndUseHashes = false; //we need to store actual files to send the data back to MIG + } + + [Given(@"a HL7 message that is exported to the test host")] + public async Task GivenAHLMessageThatIsExportedToTheTestHost() + { + HL7DestinationEntity destination; + try + { + destination = await _informaticsGatewayClient.HL7Destinations.Create(new HL7DestinationEntity + { + Name = _dicomServer.FeatureScpAeTitle, + HostIp = _hl7SendAddress, + Port = _hl7Port + }, CancellationToken.None); + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && ex.ProblemDetails.Detail.Contains("already exists")) + { + destination = await _informaticsGatewayClient.HL7Destinations.GetAeTitle(_dicomServer.FeatureScpAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + _dicomDestination = destination.Name; + + // Generate a study with multiple series + //_dataProvider.GenerateDicomData("MG", 1, 1); + await _dataProvider.GenerateHl7Messages("2.3"); + + _originalHL7Files = new Dictionary(_dataProvider.HL7Specs.Files); + + var path = "hl7filepath"; + await _dataSinkMinio.SaveHl7Async(_dataProvider, path); + + // Emit a export request event + _exportRequestEvent = new ExportRequestEvent + { + CorrelationId = _correlationId, + Destinations = new string[] { destination.Name }, + ExportTaskId = _exportTaskId, + Files = _dataProvider.HL7Specs.Files.Keys.Select(f => $"{path}/{f.Replace(".txt", ".hl7")}"), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = _workflowInstanceId, + PayloadId = "ThisIs/My/Output/Folder", + }; + + _messageToSend = new JsonMessage( + _exportRequestEvent, + MessageBrokerConfiguration.InformaticsGatewayApplicationId, + _exportRequestEvent.CorrelationId, + string.Empty); + + } + + [When(@"the HL7 Export message is received with (.*) messages acked (.*)")] + public async Task WhenTheHL7ExportMessageIsReceivedWithMessagesAcked(int messageCount, bool acked) + { + var cancellationToken = new CancellationToken(); + + _tcpListener = new System.Net.Sockets.TcpListener(IPAddress.Parse(_hl7SendAddress), _hl7Port); + _tcpListener.Start(); + + await _messagePublisher.Publish("md.export.hl7", _messageToSend.ToMessage()); + + List recievedMessages = new List(); + + for (int i = 0; i < messageCount; i++) + { + using var _client = await _tcpListener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(false); + await GetMessageAsync(_client, acked, cancellationToken); + if (_hl7Messages.Count == messageCount) + { break; } + } + + _tcpListener.Stop(); + } + + private async Task GetMessageAsync(TcpClient _client, bool acked, CancellationToken cancellationToken) + { + var messages = new List(); + using var _clientStream = _client.GetStream(); + _clientStream.ReadTimeout = 5000; + _clientStream.WriteTimeout = 5000; + var buffer = new byte[10240]; + + var s_cts = new CancellationTokenSource(); + s_cts.CancelAfter(60000); + + var bytesRead = _clientStream.Read(buffer, 0, buffer.Length); + + + if (bytesRead == 0 || s_cts.IsCancellationRequested) + { + return; + } + + var data = Encoding.UTF8.GetString(buffer.ToArray()); + + var _rawHl7Messages = HL7.Dotnetcore.MessageHelper.ExtractMessages(data); + foreach (var message in _rawHl7Messages) + { + var hl7Message = new HL7.Dotnetcore.Message(message); + hl7Message.ParseMessage(); + _hl7Messages.Add(hl7Message); + if (acked) + { await SendAcknowledgment(_clientStream, hl7Message, cancellationToken); } + } + return; + } + + [Then(@"ensure that exportcomplete messages are sent with (.*)")] + public async Task ThenEnsureThatExportcompleteMessagesAreSentWithSuscess(string valid) + { + var success = await _receivedExportHL7CompletedMessages.WaitforAsync(1, TimeSpan.FromSeconds(600)); + Assert.Equal(1, _receivedExportHL7CompletedMessages.Messages.Count); + var message = _receivedExportHL7CompletedMessages.Messages.First(); + var exportEvent = message.ConvertTo(); + var status = exportEvent.Status; + if (valid == "success") + { + Assert.Equal(ExportStatus.Success, status); + } + else if (valid == "failure") + { + Assert.Equal(ExportStatus.Failure, status); + } + + foreach (var hl7message in _hl7Messages) + { + Assertions.ShouldBeInMessageDictionary(_originalHL7Files, hl7message); + } + } + + private async Task SendAcknowledgment(NetworkStream networkStream, HL7.Dotnetcore.Message message, CancellationToken cancellationToken) + { + if (message == null) { return; } + var ackMessage = message.GetACK(true); + var ackData = new ReadOnlyMemory(ackMessage.GetMLLP()); + { + try + { + await networkStream.WriteAsync(ackData, cancellationToken).ConfigureAwait(false); + await networkStream.FlushAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception) + { + throw; + } + } + } + } +} diff --git a/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs b/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs new file mode 100755 index 000000000..55e7137cf --- /dev/null +++ b/tests/Integration.Test/StepDefinitions/RemoteAppExecutionPlugInsStepDefinitions.cs @@ -0,0 +1,325 @@ +/* + * Copyright 2022-2023 MONAI Consortium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Net; +using BoDi; +using FellowOakDicom; +using FellowOakDicom.Network; +using Monai.Deploy.InformaticsGateway.Api; +using Monai.Deploy.InformaticsGateway.Api.Models; +using Monai.Deploy.InformaticsGateway.Client; +using Monai.Deploy.InformaticsGateway.Client.Common; +using Monai.Deploy.InformaticsGateway.Configuration; +using Monai.Deploy.InformaticsGateway.Integration.Test.Common; +using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution; +using Monai.Deploy.Messaging.Events; +using Monai.Deploy.Messaging.Messages; +using Monai.Deploy.Messaging.RabbitMQ; +using Polly; +using Polly.Timeout; + +namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions +{ + [Binding] + [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] + public class RemoteAppExecutionPlugInsStepDefinitions + { + private static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromMinutes(3); + private static readonly TimeSpan DicomScpWaitTimeSpan = TimeSpan.FromMinutes(20); + private static readonly string MonaiAeTitle = "REMOTE-APPS"; + private static readonly string SourceAeTitle = "MIGTestHost"; + private static readonly DicomTag[] DicomTags = new[] { DicomTag.AccessionNumber, DicomTag.StudyDescription, DicomTag.SeriesDescription, DicomTag.PatientAddress, DicomTag.PatientAge, DicomTag.PatientName }; + private static readonly List DefaultDicomTags = new() { DicomTag.PatientID, DicomTag.StudyInstanceUID, DicomTag.SeriesInstanceUID, DicomTag.SOPInstanceUID }; + + private readonly ObjectContainer _objectContainer; + private readonly InformaticsGatewayClient _informaticsGatewayClient; + private readonly IDataClient _dataSinkMinio; + private readonly DicomScp _dicomServer; + private readonly Configurations _configuration; + private string _dicomDestination; + private readonly DataProvider _dataProvider; + private readonly RabbitMqConsumer _receivedExportCompletedMessages; + private readonly RabbitMqConsumer _receivedWorkflowRequestMessages; + private readonly RabbitMqConsumer _receivedArtifactRecievedMessages; + private readonly RabbitMQMessagePublisherService _messagePublisher; + private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; + private Dictionary _originalDicomFiles; + private ExportRequestEvent _exportRequestEvent; + private readonly Assertions _assertions; + + public RemoteAppExecutionPlugInsStepDefinitions( + ObjectContainer objectContainer, + Configurations configuration) + { + _objectContainer = objectContainer ?? throw new ArgumentNullException(nameof(objectContainer)); + _informaticsGatewayClient = objectContainer.Resolve("InformaticsGatewayClient"); + _dataSinkMinio = objectContainer.Resolve("MinioClient"); + _dicomServer = objectContainer.Resolve("DicomScp"); + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _dataProvider = objectContainer.Resolve("DataProvider"); + _receivedExportCompletedMessages = objectContainer.Resolve("ExportCompleteSubscriber"); + _receivedWorkflowRequestMessages = objectContainer.Resolve("WorkflowRequestSubscriber"); + _receivedArtifactRecievedMessages = objectContainer.Resolve("ArtifactRecievedSubscriber"); + _messagePublisher = objectContainer.Resolve("MessagingPublisher"); + _informaticsGatewayConfiguration = objectContainer.Resolve("InformaticsGatewayConfiguration"); + _assertions = objectContainer.Resolve("Assertions"); + + DefaultDicomTags.AddRange(DicomTags); + _dicomServer.ClearFilesAndUseHashes = false; //we need to store actual files to send the data back to MIG + } + + [Given(@"a study that is exported to the test host")] + public async Task AStudyThatIsExportedToTheTestHost() + { + // Register a new DICOM destination + DestinationApplicationEntity destination; + try + { + destination = await _informaticsGatewayClient.DicomDestinations.Create(new DestinationApplicationEntity + { + Name = _dicomServer.FeatureScpAeTitle, + AeTitle = _dicomServer.FeatureScpAeTitle, + HostIp = _configuration.InformaticsGatewayOptions.Host, + Port = _dicomServer.FeatureScpPort + }, CancellationToken.None); + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && ex.ProblemDetails.Detail.Contains("already exists")) + { + destination = await _informaticsGatewayClient.DicomDestinations.GetAeTitle(_dicomServer.FeatureScpAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + _dicomDestination = destination.Name; + + // Generate a study with multiple series + //_dataProvider.GenerateDicomData("MG", 1, 1); + _dataProvider.GenerateDicomData("CT", 1); + _dataProvider.InjectRandomData(DicomTags); + _originalDicomFiles = new Dictionary(_dataProvider.DicomSpecs.Files); + + await _dataSinkMinio.SendAsync(_dataProvider); + + // Emit a export request event + _exportRequestEvent = new ExportRequestEvent + { + CorrelationId = Guid.NewGuid().ToString(), + Destinations = new[] { _dicomDestination }, + ExportTaskId = Guid.NewGuid().ToString(), + Files = _dataProvider.DicomSpecs.Files.Keys.ToList(), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + PayloadId = Guid.NewGuid().ToString(), + }; + + _exportRequestEvent.PluginAssemblies.Add(typeof(DicomDeidentifier).AssemblyQualifiedName); + + var message = new JsonMessage( + _exportRequestEvent, + MessageBrokerConfiguration.InformaticsGatewayApplicationId, + _exportRequestEvent.CorrelationId, + string.Empty); + + _receivedExportCompletedMessages.ClearMessages(); + _receivedArtifactRecievedMessages.ClearMessages(); + await _messagePublisher.Publish("md.export.request.monaiscu", message.ToMessage()); + } + + [Given(@"a study that is exported to the test host with a bad plugin")] + public async Task AStudyThatIsExportedToTheTestHostBadPlugin() + { + // Register a new DICOM destination + DestinationApplicationEntity destination; + try + { + destination = await _informaticsGatewayClient.DicomDestinations.Create(new DestinationApplicationEntity + { + Name = _dicomServer.FeatureScpAeTitle, + AeTitle = _dicomServer.FeatureScpAeTitle, + HostIp = _configuration.InformaticsGatewayOptions.Host, + Port = _dicomServer.FeatureScpPort + }, CancellationToken.None); + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && ex.ProblemDetails.Detail.Contains("already exists")) + { + destination = await _informaticsGatewayClient.DicomDestinations.GetAeTitle(_dicomServer.FeatureScpAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + _dicomDestination = destination.Name; + + // Generate a study with multiple series + //_dataProvider.GenerateDicomData("MG", 1, 1); + _dataProvider.GenerateDicomData("CT", 1); + _dataProvider.InjectRandomData(DicomTags); + _originalDicomFiles = new Dictionary(_dataProvider.DicomSpecs.Files); + + await _dataSinkMinio.SendAsync(_dataProvider); + + // send 2 messagees the first on should fail, the second one should not + string pluginName = typeof(DicomDeidentifier).AssemblyQualifiedName; + pluginName = pluginName.Replace("DicomDeidentifier", "fail"); + + for (int i = 0; i < 2; ++i) + { + + // Emit a export request event + _exportRequestEvent = new ExportRequestEvent + { + CorrelationId = Guid.NewGuid().ToString(), + Destinations = new[] { _dicomDestination }, + ExportTaskId = Guid.NewGuid().ToString(), + Files = _dataProvider.DicomSpecs.Files.Keys.ToList(), + MessageId = Guid.NewGuid().ToString(), + WorkflowInstanceId = Guid.NewGuid().ToString(), + PayloadId = Guid.NewGuid().ToString(), + }; + _exportRequestEvent.PluginAssemblies.Add(pluginName); + var message = new JsonMessage( + _exportRequestEvent, + MessageBrokerConfiguration.InformaticsGatewayApplicationId, + _exportRequestEvent.CorrelationId, + string.Empty); + + _receivedExportCompletedMessages.ClearMessages(); + await _messagePublisher.Publish("md.export.request.monaiscu", message.ToMessage()); + pluginName = typeof(DicomDeidentifier).AssemblyQualifiedName; + } + } + + [When(@"the study is received and sent back to Informatics Gateway with (.*) messages")] + public async Task WhenTheStudyIsReceivedAndSentBackToInformaticsGatewayWith(int exportCount) + { + + // setup DICOM Source + try + { + await _informaticsGatewayClient.DicomSources.Create(new SourceApplicationEntity + { + Name = SourceAeTitle, + AeTitle = SourceAeTitle, + HostIp = _configuration.InformaticsGatewayOptions.Host, + }, CancellationToken.None); + _dataProvider.Source = SourceAeTitle; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.DicomSources.GetAeTitle(SourceAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + + // setup MONAI Deploy AET + _dataProvider.StudyGrouping = "0020,000D"; + try + { + await _informaticsGatewayClient.MonaiScpAeTitle.Create(new MonaiApplicationEntity + { + AeTitle = MonaiAeTitle, + Name = MonaiAeTitle, + Grouping = _dataProvider.StudyGrouping, + Timeout = 3, + PlugInAssemblies = new List() { typeof(DicomReidentifier).AssemblyQualifiedName } + }, CancellationToken.None); + _dataProvider.Destination = MonaiAeTitle; + } + catch (ProblemException ex) + { + if (ex.ProblemDetails.Status == (int)HttpStatusCode.Conflict && + ex.ProblemDetails.Detail.Contains("already exists")) + { + await _informaticsGatewayClient.MonaiScpAeTitle.GetAeTitle(MonaiAeTitle, CancellationToken.None); + } + else + { + throw; + } + } + + var timeoutPolicy = Policy.TimeoutAsync(240, TimeoutStrategy.Pessimistic); + await timeoutPolicy + .ExecuteAsync( + async () => { await SendRequest(exportCount); } + ); + + // Clear workflow request messages + _receivedWorkflowRequestMessages.ClearMessages(); + _receivedArtifactRecievedMessages.ClearMessages(); + + _dataProvider.DimseRsponse.Should().Be(DicomStatus.Success); + + // Wait for workflow request events + (await _receivedArtifactRecievedMessages.WaitforAsync(1, MessageWaitTimeSpan)).Should().BeTrue(); + _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, DataService.DIMSE, _receivedArtifactRecievedMessages.Messages, 1); + } + + private async Task SendRequest(int exportCount = 1) + { + // Wait for export completed event + (await _receivedExportCompletedMessages.WaitforAsync(exportCount, DicomScpWaitTimeSpan)).Should().BeTrue(); + + foreach (var key in _dataProvider.DicomSpecs.FileHashes.Keys) + { + (await Extensions.WaitUntil(() => _dicomServer.Instances.ContainsKey(key), DicomScpWaitTimeSpan)).Should().BeTrue("{0} should be received", key); + } + + // Send data received back to MIG + var storeScu = _objectContainer.Resolve("StoreSCU"); + + var host = _configuration.InformaticsGatewayOptions.Host; + var port = _informaticsGatewayConfiguration.Dicom.Scp.Port; + + _dataProvider.Workflows = null; + _dataProvider.DicomSpecs.Files.Clear(); + _dataProvider.DicomSpecs.Files = new Dictionary(_dicomServer.DicomFiles); + _dataProvider.DicomSpecs.Files.Should().NotBeNull(); + + await storeScu.SendAsync( + _dataProvider, + SourceAeTitle, + host, + port, + MonaiAeTitle); + } + + [Then(@"ensure the original study and the received study are the same")] + public async Task EnsureTheOriginalStudyAndTheReceivedStudyAreTheSameAsync() + { + var workflowRequestEvent = _receivedArtifactRecievedMessages.Messages[0].ConvertTo(); + _exportRequestEvent.CorrelationId.Should().Be(_receivedArtifactRecievedMessages.Messages[0].CorrelationId); + _exportRequestEvent.CorrelationId.Should().Be(workflowRequestEvent.CorrelationId); + _exportRequestEvent.WorkflowInstanceId.Should().Be(workflowRequestEvent.WorkflowInstanceId); + _exportRequestEvent.ExportTaskId.Should().Be(workflowRequestEvent.TaskId); + await _assertions.ShouldRestoreAllDicomMetaata(_receivedArtifactRecievedMessages.Messages, _originalDicomFiles, DefaultDicomTags.ToArray()).ConfigureAwait(false); + } + } +} diff --git a/tests/Integration.Test/StepDefinitions/SharedDefinitions.cs b/tests/Integration.Test/StepDefinitions/SharedDefinitions.cs index 7c362328a..ed5de2692 100644 --- a/tests/Integration.Test/StepDefinitions/SharedDefinitions.cs +++ b/tests/Integration.Test/StepDefinitions/SharedDefinitions.cs @@ -1,5 +1,5 @@ /* - * Copyright 2022 MONAI Consortium + * Copyright 2022-2023 MONAI Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ using Monai.Deploy.InformaticsGateway.Configuration; using Monai.Deploy.InformaticsGateway.Integration.Test.Common; using Monai.Deploy.InformaticsGateway.Integration.Test.Drivers; +using Monai.Deploy.InformaticsGateway.Test.PlugIns; namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions { @@ -26,7 +27,6 @@ namespace Monai.Deploy.InformaticsGateway.Integration.Test.StepDefinitions [CollectionDefinition("SpecFlowNonParallelizableFeatures", DisableParallelization = true)] public class SharedDefinitions { - internal static readonly TimeSpan MessageWaitTimeSpan = TimeSpan.FromMinutes(10); private readonly InformaticsGatewayConfiguration _informaticsGatewayConfiguration; private readonly RabbitMqConsumer _receivedMessages; private readonly Assertions _assertions; @@ -48,28 +48,31 @@ public SharedDefinitions(ObjectContainer objectContainer) [Given(@"(.*) (.*) studies")] public void GivenNStudies(int studyCount, string modality) { - Guard.Against.NegativeOrZero(studyCount); - Guard.Against.NullOrWhiteSpace(modality); + Guard.Against.NegativeOrZero(studyCount, nameof(studyCount)); + Guard.Against.NullOrWhiteSpace(modality, nameof(modality)); _dataProvider.GenerateDicomData(modality, studyCount); - _receivedMessages.SetupMessageHandle(_dataProvider.DicomSpecs.NumberOfExpectedRequests(_dataProvider.StudyGrouping)); - } - - [Then(@"(.*) workflow requests sent to message broker")] - public void ThenWorkflowRequestSentToMessageBroker(int workflowCount) - { - Guard.Against.NegativeOrZero(workflowCount); - - _receivedMessages.MessageWaitHandle.Wait(MessageWaitTimeSpan).Should().BeTrue(); - _assertions.ShouldHaveCorrectNumberOfWorkflowRequestMessages(_dataProvider, _receivedMessages.Messages, workflowCount); + _receivedMessages.ClearMessages(); } [Then(@"studies are uploaded to storage service")] public async Task ThenXXFilesUploadedToStorageService() { - _receivedMessages.MessageWaitHandle.Wait(MessageWaitTimeSpan).Should().BeTrue(); await _assertions.ShouldHaveUploadedDicomDataToMinio(_receivedMessages.Messages, _dataProvider.DicomSpecs.FileHashes); } + + [Then(@"studies are uploaded to storage service with data input plugins")] + public async Task ThenXXFilesUploadedToStorageServiceWithDataInputPlugIns() + { + await _assertions.ShouldHaveUploadedDicomDataToMinio( + _receivedMessages.Messages, + _dataProvider.DicomSpecs.FileHashes, + (dicomFile) => + { + dicomFile.Dataset.GetString(TestInputDataPlugInModifyDicomFile.ExpectedTag) + .Should().Be(TestInputDataPlugInModifyDicomFile.ExpectedValue); + }); + } } } diff --git a/tests/Integration.Test/appsettings.json b/tests/Integration.Test/appsettings.json old mode 100644 new mode 100755 index ad6e43660..2a702e5ea --- a/tests/Integration.Test/appsettings.json +++ b/tests/Integration.Test/appsettings.json @@ -1,13 +1,50 @@ { + "MonaiDeployAuthentication": { + "BypassAuthentication": true + }, + "plugins": { + "remoteApp": { + "ReplaceTags": "AccessionNumber, StudyDescription, SeriesDescription, PatientAddress, PatientAge, PatientName" + }, + "Pseudonymise": { + "ConnectionString": "mongodb://root:rootpassword@localhost:27017", + "DatabaseName": "InformaticsGateway", + "EncriptionClientTimeoutSeconds": "900", + "ExpiresAfterDays": "1", + "ExternalAppTaskInboundKeepTags": "0020 000E, 0020 0010, 0020 00013, 0008 0018, 0020 000E", + "ImportantTags": "0010 0020, 0008 0018, 0008 0016, 0020 000D, 0020 000E, 0020 0010,0008 1155, 0008 0014, 0008 0050, 0008 0080, 0008 0081, 0008 0090, 0008 0092, 0008 0094, 0008 1010, 0008 1030, 0008 103E, 0008 1040, 0008 1048,0008 1050, 0008 1060, 0008 1070, 0008 1080, 0008 2111, 0010 0010, 0010 0030, 0010 0030, 0010 0032, 0010 0040, 0010 1000, 0010 1001, 0010 1001, 0010 1010, 0010 1020, 0010 1030, 0010 1090, 0010 2160, 0010 2180, 0010 21B0, 0010 4000, 0018 1000, 0018 1030, 0020 0052, 0020 0200, 0020 4000, 0040 0275, 0040 A124, 0040 A730, 0088 0140, 3006 0024, 3006 00C2", + "SecurityProfile": "\t\t\t\t0010,0020;K;;;;;;;;;;\r\n\t\t\t\t0008,0018;C;;;;;;;;;;\r\n\t\t\t\t0008,0016;K;;;;;;;;;;\r\n\t\t\t\t0020,000D;K;;;;;;;;;;\r\n\t\t\t\t0020,000E;C;;;;;;;;;;\r\n\t\t\t\t0020,0010;C;;;;;;;;;;\r\n\t\t\t\t0008,1155;C;;;;;;;;;;\r\n\t\t\t\t0008,0014;X;;;;;;;;;;\r\n\t\t\t\t0008,0050;K;;;;;;;;;;\r\n\t\t\t\t0008,0080;X;;;;;;;;;;\r\n\t\t\t\t0008,0081;X;;;;;;;;;;\r\n\t\t\t\t0008,0090;X;;;;;;;;;;\r\n\t\t\t\t0008,0092;X;;;;;;;;;;\r\n\t\t\t\t0008,0094;X;;;;;;;;;;\r\n\t\t\t\t0008,1010;X;;;;;;;;;;\r\n\t\t\t\t0008,1030;X;;;;;;;;;;\r\n\t\t\t\t0008,103E;X;;;;;;;;;;\r\n\t\t\t\t0008,1040;X;;;;;;;;;;\r\n\t\t\t\t0008,1048;X;;;;;;;;;;\r\n\t\t\t\t0008,1050;X;;;;;;;;;;\r\n\t\t\t\t0008,1060;X;;;;;;;;;;\r\n\t\t\t\t0008,1070;X;;;;;;;;;;\r\n\t\t\t\t0008,1080;X;;;;;;;;;;\r\n\t\t\t\t0008,2111;X;;;;;;;;;;\r\n\t\t\t\t0010,0010;X;;;;;;;;;;\r\n\t\t\t\t0010,0030;X;;;;;;;;;;\r\n\t\t\t\t0010,0030;X;;;;;;;;;;\r\n\t\t\t\t0010,0032;X;;;;;;;;;;\r\n\t\t\t\t0010,0040;X;;;;;;;;;;\r\n\t\t\t\t0010,1000;X;;;;;;;;;;\r\n\t\t\t\t0010,1001;X;;;;;;;;;;\r\n\t\t\t\t0010,1001;X;;;;;;;;;;\r\n\t\t\t\t0010,1010;X;;;;;;;;;;\r\n\t\t\t\t0010,1020;X;;;;;;;;;;\r\n\t\t\t\t0010,1030;X;;;;;;;;;;\r\n\t\t\t\t0010,1090;X;;;;;;;;;;\r\n\t\t\t\t0010,2160;X;;;;;;;;;;\r\n\t\t\t\t0010,2180;X;;;;;;;;;;\r\n\t\t\t\t0010,21B0;X;;;;;;;;;;\r\n\t\t\t\t0010,4000;X;;;;;;;;;;\r\n\t\t\t\t\r\n\t\t\t\t0018,1000;X;;;;;;;;;;\r\n\t\t\t\t0018,1030;X;;;;;;;;;;\r\n\t\t\t\t0020,0052;X;;;;;;;;;;\r\n\t\t\t\t0020,0200;X;;;;;;;;;;\r\n\t\t\t\t0020,4000;X;;;;;;;;;;\r\n\t\t\t\t0040,0275;X;;;;;;;;;;\r\n\t\t\t\t0040,A124;X;;;;;;;;;;\r\n\t\t\t\t0040,A730;X;;;;;;;;;;\r\n\t\t\t\t0088,0140;X;;;;;;;;;;\r\n\t\t\t\t3006,0024;X;;;;;;;;;;\r\n\t\t\t\t3006,00C2;X;;;;;;;;;;\r\n\t\t\t\t", + "KMS": { + "KeyVaultNamespace": "InformaticsGateway.KeyVault", + "AWS": { + "accessKeyId": "", + "arnKey": "", + "roleArnToAssume": "", + "region": "eu-west-2", + "secretAccessKey": "" + } + } + } + }, "ConnectionStrings": { - "Type": "mongodb", - "InformaticsGatewayDatabase": "mongodb://root:rootpassword@localhost:27017", - "DatabaseName": "InformaticsGateway" + "Type": "sqlite", + "InformaticsGatewayDatabase": "Data Source=./mig.db", + "DatabaseOptions": { + "DatabaseName": "InformaticsGateway", + "retries": { + "delays": [ + "750", + "1201", + "2500" + ] + } + } }, "InformaticsGateway": { "dicom": { "scp": { "port": 1104, + "externalAppPort": 1106, "logDimseDatasets": false, "rejectUnknownSources": true }, @@ -17,6 +54,10 @@ "logDataPDUs": false } }, + "dicomWeb": { + "plugins": [ "Monai.Deploy.InformaticsGateway.Test.PlugIns.TestInputDataPlugInModifyDicomFile, Monai.Deploy.InformaticsGateway.Test.PlugIns" ], + "timeout": 10 + }, "messaging": { "publisherServiceAssemblyName": "Monai.Deploy.Messaging.RabbitMQ.RabbitMQMessagePublisherService, Monai.Deploy.Messaging.RabbitMQ", "publisherSettings": { @@ -59,7 +100,7 @@ "hl7": { "port": 2575, "maximumNumberOfConnections": 10, - "clientTimeout": 60000, + "clientTimeout": 200, "sendAck": true } }, diff --git a/tests/Integration.Test/appsettings.mongodb.json b/tests/Integration.Test/appsettings.mongodb.json index 93e9973e5..59d8b044c 100644 --- a/tests/Integration.Test/appsettings.mongodb.json +++ b/tests/Integration.Test/appsettings.mongodb.json @@ -2,6 +2,15 @@ "ConnectionStrings": { "Type": "mongodb", "InformaticsGatewayDatabase": "mongodb://root:rootpassword@localhost:27017", - "DatabaseName": "InformaticsGateway" + "DatabaseOptions": { + "DatabaseName": "InformaticsGateway", + "retries": { + "delays": [ + "750", + "1201", + "2500" + ] + } + } } } \ No newline at end of file diff --git a/tests/Integration.Test/debug.sh b/tests/Integration.Test/debug.sh index 5edaeeab9..2331136a0 100755 --- a/tests/Integration.Test/debug.sh +++ b/tests/Integration.Test/debug.sh @@ -20,7 +20,7 @@ export SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" TEST_DIR="$SCRIPT_DIR/" LOG_DIR="${GITHUB_WORKSPACE:-$SCRIPT_DIR}" RUN_DIR="$SCRIPT_DIR/.run" -BIN_DIR="$TEST_DIR/bin/Release/net6.0" +BIN_DIR="$TEST_DIR/bin/Release/net8.0" CONFIG_DIR="$SCRIPT_DIR/configs" EXIT=false METRICSFILE="$LOG_DIR/metrics.log" diff --git a/tests/Integration.Test/nlog.config b/tests/Integration.Test/nlog.config index b987aba2f..ed0056cd5 100644 --- a/tests/Integration.Test/nlog.config +++ b/tests/Integration.Test/nlog.config @@ -23,8 +23,8 @@ limitations under the License. internalLogFile="${basedir}/logs/internal-nlog.txt"> - - + + @@ -35,7 +35,6 @@ limitations under the License. - @@ -45,15 +44,14 @@ limitations under the License. - - - - - - + + + + + + - @@ -71,11 +69,10 @@ limitations under the License. - + - diff --git a/tests/Integration.Test/packages.lock.json b/tests/Integration.Test/packages.lock.json index bdb8c7a3a..18eba6040 100644 --- a/tests/Integration.Test/packages.lock.json +++ b/tests/Integration.Test/packages.lock.json @@ -1,181 +1,181 @@ { "version": 1, "dependencies": { - "net6.0": { + "net8.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, "FluentAssertions": { "type": "Direct", - "requested": "[6.8.0, )", - "resolved": "6.8.0", - "contentHash": "NfSlAG97wMxS48Ov+wQEhJITdn4bKrgtKrG4sCPrFBVKozpC57lQ2vzsPdxUOsPbfEgEQTMtvCDECxIlDBfgNA==", + "requested": "[6.12.2, )", + "resolved": "6.12.2", + "contentHash": "8YE+xJmT8wgzEpFuzJ4S62oFhEL/AKouMz1RWPEMEUhy9H11aRQlGIWcHurH5BEy7tbF6gb0CJrs0wOw/AtDcQ==", "dependencies": { "System.Configuration.ConfigurationManager": "4.4.0" } }, "fo-dicom": { "type": "Direct", - "requested": "[5.0.3, )", - "resolved": "5.0.3", - "contentHash": "OPkCQ9+X/fvGRokAAgjR8bOpai04qlnNHmq+LsgI+Kyug3yar2zk6IMOSSvPOLgWe0EG9ScdqH44AGKnviH5Rw==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Toolkit.HighPerformance": "7.1.2", + "requested": "[5.2.1, )", + "resolved": "5.2.1", + "contentHash": "Oa6raonOj/Xm+a1j3O89OlUXJIF55jLAKjCuXKINYJMJ+hJ/9Al1YOxPs1hut8DBKvHbgYtgdRFtqGNS+Qt6Uw==", + "dependencies": { + "CommunityToolkit.HighPerformance": "8.3.2", + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "Microsoft.Bcl.HashCode": "1.1.1", + "Microsoft.Extensions.DependencyInjection": "6.0.1", + "Microsoft.Extensions.Logging": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0", "System.Buffers": "4.5.1", - "System.Text.Encoding.CodePages": "4.6.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", + "System.Text.Encoding.CodePages": "6.0.1", + "System.Text.Encodings.Web": "8.0.0", + "System.Text.Json": "8.0.5", "System.Threading.Channels": "6.0.0" } }, "HL7-dotnetcore": { "type": "Direct", - "requested": "[2.29.0, )", - "resolved": "2.29.0", - "contentHash": "E0N/W72HsvIJj6XGyiUv9BHmyhvkNedpa23QN/Xwk47S965NYC9JSA1VVYWAAs4J6yOIhpM3lBOEWvhQBO31Lw==" + "requested": "[2.39.1, )", + "resolved": "2.39.1", + "contentHash": "xbgykLlAr644SfYY2oxnpYYto+JFYLuUHt00hRTlhpB+5DiFC2oVSVRA+8yyq/lmkjTNidtTbN9nyK0ft4DSfw==" }, "Microsoft.EntityFrameworkCore": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "eUsIZ52uBJFCr/OUL1EHp0BAwdkfHFVGMyXYrkGUjkSWtPd751wgFzgWBstxOQYzUEyKtz1/wC72S8Db0vPvsg==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "HNn+NPKCm7rR7ij7IRCCbuImaMulFJGloyIbMwi3Ews77RsthM8gxpTZciFLgRYPsBtszKpdIClEwnWmP0vjUg==", "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Microsoft.EntityFrameworkCore.Analyzers": "6.0.11", - "Microsoft.Extensions.Caching.Memory": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "System.Collections.Immutable": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.EntityFrameworkCore.Abstractions": "8.0.14", + "Microsoft.EntityFrameworkCore.Analyzers": "8.0.14", + "Microsoft.Extensions.Caching.Memory": "8.0.1", + "Microsoft.Extensions.Logging": "8.0.1" } }, "Microsoft.EntityFrameworkCore.Sqlite": { "type": "Direct", - "requested": "[6.0.11, )", - "resolved": "6.0.11", - "contentHash": "F5db018VdecebRNbRdk6sB2P9nCRmcVncp53IFivJhzVGWB6ogCXdRgkEak2KGSM6J8zPFiGpSUQYd3EIS4F0g==", + "requested": "[8.0.14, )", + "resolved": "8.0.14", + "contentHash": "iqrhkOirZ9mm3Yu+ut9698VDn6WSykfr9NMECIe6gObUZLxAsg28f1JmIjx2n4pKFm5Uz5sYJ3k4AUnrJbgUag==", "dependencies": { - "Microsoft.EntityFrameworkCore.Sqlite.Core": "6.0.11", - "SQLitePCLRaw.bundle_e_sqlite3": "2.0.6" + "Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.14", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.6" } }, "Microsoft.Extensions.Configuration": { "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "BUyFU9t+HzlSE7ri4B+AQN2BgTgHv/uM82s5ZkgU1BApyzWzIl48nDsG5wR1t0pniNuuyTBzG3qCW8152/NtSw==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "b3ErKzND8LIC7o08QAVlKfaEIYEvLJbtmVbFZVBRXeu9YkKfSSzLZfR1SUfQPBIy9mKLhEtJgGYImkcMNaKE0A==", + "requested": "[8.0.2, )", + "resolved": "8.0.2", + "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.EnvironmentVariables": { "type": "Direct", - "requested": "[6.0.1, )", - "resolved": "6.0.1", - "contentHash": "pnyXV1LFOsYjGveuC07xp0YHIyGq7jRq5Ncb5zrrIieMLWVwgMyYxcOH0jTnBedDT4Gh1QinSqsjqzcieHk1og==", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.Extensions.Configuration.Json": { "type": "Direct", - "requested": "[6.0.0, )", - "resolved": "6.0.0", - "contentHash": "GJGery6QytCzS/BxJ96klgG9in3uH26KcUBbiVG/coNDXCRq6LGVVlUT4vXq34KPuM+R2av+LeYdX9h4IZOCUg==", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0" } }, "Microsoft.NET.Test.Sdk": { "type": "Direct", - "requested": "[17.4.0, )", - "resolved": "17.4.0", - "contentHash": "VtNZQ83ntG2aEUjy1gq6B4HNdn96se6FmdY/03At8WiqDReGrApm6OB2fNiSHz9D6IIEtWtNZ2FSH0RJDVXl/w==", + "requested": "[17.13.0, )", + "resolved": "17.13.0", + "contentHash": "W19wCPizaIC9Zh47w8wWI/yxuqR7/dtABwOrc8r2jX/8mUNxM2vw4fXDh+DJTeogxV+KzKwg5jNNGQVwf3LXyA==", "dependencies": { - "Microsoft.CodeCoverage": "17.4.0", - "Microsoft.TestPlatform.TestHost": "17.4.0" + "Microsoft.CodeCoverage": "17.13.0", + "Microsoft.TestPlatform.TestHost": "17.13.0" } }, "Minio": { "type": "Direct", - "requested": "[4.0.6, )", - "resolved": "4.0.6", - "contentHash": "c/7hO1I0/PB1hJr7HXuNxsTT59xi4ADPxHhGtv7zzTNZqDqq+Vgv+C8xSJ6rlIy4px7ibhMt6Kunq20ZBlLj4Q==", + "requested": "[6.0.2, )", + "resolved": "6.0.2", + "contentHash": "4Od4uGANX5X0AL90WV0viBNzpE2+jDHro6CGvR4//MVj5SiTTwR5SUikXgd/2G2PtYyXw4b/IBpo7Kt5cCCndA==", "dependencies": { - "Crc32.NET": "1.2.0", - "Microsoft.CSharp": "4.7.0", - "Newtonsoft.Json": "13.0.1", - "System.Net.Http": "4.3.4", - "System.Net.Primitives": "4.3.1", - "System.Reactive.Linq": "5.0.0", - "System.ValueTuple": "4.4.0" + "CommunityToolkit.HighPerformance": "8.2.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "System.IO.Hashing": "8.0.0", + "System.Reactive": "6.0.0" } }, "Monai.Deploy.Messaging.RabbitMQ": { "type": "Direct", - "requested": "[0.1.16, )", - "resolved": "0.1.16", - "contentHash": "P7JlaepxiuUxY3GmKhKnhkDKaHID5KFEE//fIwUkhS39WMYLzq3K63URzM/8EIPmuZQnxl2n7EEZVxafY4FHQg==", + "requested": "[2.0.4, )", + "resolved": "2.0.4", + "contentHash": "tmEERdnqJq1STVGx30+wVyj7AtvtT8K2j/59lSiZ7k58ZdRku601Gs6k1KZ/sfGeobCjaB4gI7GROxK+lN6gbg==", "dependencies": { - "Monai.Deploy.Messaging": "0.1.16", - "Polly": "7.2.3", - "RabbitMQ.Client": "6.4.0", - "System.Collections.Concurrent": "4.3.0" + "Monai.Deploy.Messaging": "2.0.4", + "Polly": "8.5.2", + "RabbitMQ.Client": "6.8.1" } }, "Monai.Deploy.Storage.MinIO": { "type": "Direct", - "requested": "[0.2.10, )", - "resolved": "0.2.10", - "contentHash": "k6j9u4x0gcMml2b5wUufItGvA8YqDnw7utxjsG1wSF7lGsqs5R5ajr9m6Z9//fSzFQ7bn1iMexnDLDhLLmo+eQ==", + "requested": "[1.0.2, )", + "resolved": "1.0.2", + "contentHash": "H73iUS64UwZXK8ZxNUc3DvokEcptpcoYgY+FMJGlotCffGhqrK8iJ18THR0gg9Io5UgIv24mdu1jbUn/Cz2rhA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Minio": "4.0.6", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.S3Policy": "0.2.10" + "Minio": "6.0.2", + "Monai.Deploy.Storage": "1.0.2", + "Monai.Deploy.Storage.S3Policy": "1.0.2" } }, "Moq": { "type": "Direct", - "requested": "[4.18.2, )", - "resolved": "4.18.2", - "contentHash": "SjxKYS5nX6prcaT8ZjbkONh3vnh0Rxru09+gQ1a07v4TM530Oe/jq3Q4dOZPfo1wq0LYmTgLOZKrqRfEx4auPw==", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", "dependencies": { - "Castle.Core": "5.1.0" + "Castle.Core": "5.1.1" } }, "Polly": { "type": "Direct", - "requested": "[7.2.3, )", - "resolved": "7.2.3", - "contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ==" + "requested": "[8.5.2, )", + "resolved": "8.5.2", + "contentHash": "vbXsGgkG86nG+TOwY+SmtrGrRHmHH0DQaxtILx//d3Dz/ocJ8izSNYzdvU2gEtWa/LDD8zJLvD3HdjEkdlvkhg==", + "dependencies": { + "Polly.Core": "8.5.2" + } }, "RabbitMQ.Client": { "type": "Direct", - "requested": "[6.4.0, )", - "resolved": "6.4.0", - "contentHash": "1znR1gGU+xYVSpO5z8nQolcUKA/yydnxQn7Ug9+RUXxTSLMm/eE58VKGwahPBjELXvDnX0k/kBrAitFLRjx9LA==", + "requested": "[6.8.1, )", + "resolved": "6.8.1", + "contentHash": "jNsmGgmCNw2S/NzskeN2ijtGywtH4Sk/G6jWUTD5sY9SrC27Xz6BsLIiB8hdsfjeyWCa4j4GvCIGkpE8wrjU1Q==", "dependencies": { - "System.Memory": "4.5.4", - "System.Threading.Channels": "4.7.1" + "System.Memory": "4.5.5", + "System.Threading.Channels": "7.0.0" } }, "SpecFlow.Plus.LivingDocPlugin": { @@ -212,40 +212,56 @@ }, "xunit": { "type": "Direct", - "requested": "[2.4.2, )", - "resolved": "2.4.2", - "contentHash": "6Mj73Ont3zj2CJuoykVJfE0ZmRwn7C+pTuRP8c4bnaaTFjwNG6tGe0prJ1yIbMe9AHrpDys63ctWacSsFJWK/w==", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", "dependencies": { - "xunit.analyzers": "1.0.0", - "xunit.assert": "2.4.2", - "xunit.core": "[2.4.2]" + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" } }, "xunit.runner.visualstudio": { "type": "Direct", - "requested": "[2.4.5, )", - "resolved": "2.4.5", - "contentHash": "OwHamvBdUKgqsXfBzWiCW/O98BTx81UKzx2bieIOQI7CZFE5NEQZGi8PBQGIKawDW96xeRffiNf20SjfC0x9hw==" + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" + }, + "AnswerDicomTools": { + "type": "Transitive", + "resolved": "0.1.1-rc0089", + "contentHash": "DgDBjo708kHmr0lUdUrYaRLjmaICrJMBh6w/Vd0E/r2SJ0DDiQuxMABJzIwXwrVFSzrrwpqPqWBQMTCY++9uPQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "6.0.1", + "MongoDB.Driver": "2.21.0", + "fo-dicom": "5.1.1" + } }, "Ardalis.GuardClauses": { "type": "Transitive", - "resolved": "4.0.1", - "contentHash": "RemnImQf/BWR8oYqFpdw+hn+b4Q1w+pGujkRiSfjQhMPuiERwGn4UMmQv+6UDE4qbPlnIN+e3e40JkvBhzgfzg==", + "resolved": "4.6.0", + "contentHash": "ckLr6V8tRXHu0kGVlen70sjXkZcbe6ZFknM3UDkeNzY75kXEe4Z5xru5weMU5IEAsuuqMKvVb7ikbUZ7JcHUSQ==" + }, + "AspNetCore.HealthChecks.MongoDb": { + "type": "Transitive", + "resolved": "8.1.0", + "contentHash": "NuVDrj7UXBVniePh6JnuM8ryZRWdOIGOBes3owg2WQV/1NPntpWqX/DYaP6SBduHULUp8XRbwAui8qKQAW4SDA==", "dependencies": { - "JetBrains.Annotations": "2021.3.0" + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "MongoDB.Driver": "2.28.0" } }, "AWSSDK.Core": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "TABd2IP9MUPtoLJ3EouOsZ1RfNqFPz0w7pblWcaXMw8BgaLSH4xWD7uX+0oIhRVs0GalIl3RHZEjOibEGezDUA==" + "resolved": "3.7.402.25", + "contentHash": "NCbho/muk9knZ70dOlKBhIB0WLxKwg/TzElYj5jVBJUEFx/p/lmGhMvTEGof42Xtr1VjJ0FdZDPl4BarRaKHQA==" }, "AWSSDK.SecurityToken": { "type": "Transitive", - "resolved": "3.7.100.6", - "contentHash": "8aTiY7DxAkq6kqdipWBJ7O7XDMABPMevJSFYtOxhjjllW8hkwOY3f5R1ff2ZFSRA5H96xsBLLj/66gc+hmVweQ==", + "resolved": "3.7.401.68", + "contentHash": "mtYLPlgG9VHYONevNMIP+ALY99ufbP1+fAuT9C2lCtofY0DIR5SZa+tyGKiVni+mDYJSq66Bdqqa7i/D8USYLA==", "dependencies": { - "AWSSDK.Core": "[3.7.100.6, 4.0.0)" + "AWSSDK.Core": "[3.7.402.25, 4.0.0)" } }, "BoDi": { @@ -255,19 +271,16 @@ }, "Castle.Core": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "31UJpTHOiWq95CDOHazE3Ub/hE/PydNWsJMwnEVTqFFP4WhAugwpaVGxzOxKgNeSUUeqS2W6lxV+q7u1pAOfXg==", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", "dependencies": { "System.Diagnostics.EventLog": "6.0.0" } }, - "Crc32.NET": { + "CommunityToolkit.HighPerformance": { "type": "Transitive", - "resolved": "1.2.0", - "contentHash": "wNW/huzolu8MNKUnwCVKxjfAlCFpeI8AZVfF46iAWJ1+P6bTU1AZct7VAkDDEjgeeTJCVTkGZaD6jSd/fOiUkA==", - "dependencies": { - "NETStandard.Library": "2.0.0" - } + "resolved": "8.3.2", + "contentHash": "1Os81ua0FmIOtiSgOk5C1KBraQ3SDfxs/7BG4qDagm48nGplr//lAVqLH9I2TLDVqRFdhqTUaEITFA5Ho/Ovkw==" }, "DnsClient": { "type": "Transitive", @@ -279,115 +292,168 @@ }, "DotNext": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "5Xp6G9U0MhSmfgxKklUUsOFfSg2VqF+/rkd7WyoUs7HqbnVd32bRw2rWW5o+rieHLzUlW/sagctPiaZqmeTA+g==", + "resolved": "5.3.1", + "contentHash": "PIkzfT1wsvWGXb4Zigpfb7kUfNQdmlKi57yXYeMRE+06/JzpruwrBnQvwtKGw0qgn9lM4EIXyVfo4gnUARFDRw==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.IO.Hashing": "8.0.0" } }, "DotNext.Threading": { "type": "Transitive", - "resolved": "4.7.4", - "contentHash": "G/AogSunqiZZ/0H4y3Qy/YNveIB+6azddStmFxbxLWkruXZ27gXyoRQ9kQ2gpDbq/+YfMINz9nmTY5ZtuCzuyw==", + "resolved": "5.5.0", + "contentHash": "djd6MPTZA3n2VcpViD0D03tkReudsCWpAefIhiRQCWEgTcW0uW1cZqlTh7qX15Jd5sKBE9qB13mJsrchpu3MKQ==", "dependencies": { - "DotNext": "4.7.4", - "System.Threading.Channels": "6.0.0" + "DotNext": "5.3.1", + "System.Threading.Channels": "8.0.0" } }, - "fo-dicom.NLog": { + "Gherkin": { + "type": "Transitive", + "resolved": "19.0.3", + "contentHash": "kq9feqMojMj9aABrHb/ABEPaH2Y4dSclseSahAru6qxCeqVQNLLTgw/6vZMauzI1yBUL2fz03ub5yEd5btLfvg==" + }, + "Humanizer.Core": { "type": "Transitive", - "resolved": "5.0.3", - "contentHash": "k35FD+C9IcpTLjCF5tvCkBGUxJ+YvzoBsgb2VAtGQv+aVTu+HyoCnNVqccc4lVE53fbVCwpR3gPiTAnm5fm+KQ==", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Macross.Json.Extensions": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + }, + "Microsoft.AspNetCore.Authentication.JwtBearer": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "e19jmWJAQucbPYk3/fihJMDCYfv6CO+Qwp34pOehUSCbaHROw6FZ551SN1D0s9Btl0U/QHfuwFq6Z8Oa2ktE6g==", "dependencies": { - "NLog": "4.7.11", - "fo-dicom": "5.0.3" + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2" } }, - "Gherkin": { + "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", - "resolved": "19.0.3", - "contentHash": "kq9feqMojMj9aABrHb/ABEPaH2Y4dSclseSahAru6qxCeqVQNLLTgw/6vZMauzI1yBUL2fz03ub5yEd5btLfvg==" + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" }, - "JetBrains.Annotations": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "2021.3.0", - "contentHash": "Ddxjs5RRjf+c8m9m++WvhW1lz1bqNhsTjWvCLbQN9bvKbkJeR9MhtfNwKgBRRdG2yLHcXFr5Lf7fsvvkiPaDRg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Karambolo.Extensions.Logging.File": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "3.3.1", - "contentHash": "wkPTc/UEuSAwbO3/Ee+oCdotxncmc/DKwjM533Z0BKvJm94NLOvU2i7pifgMd6uAUJ8jy69OcFZRu7hXKbMW6g==", + "resolved": "3.3.3", + "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==", "dependencies": { - "Microsoft.Extensions.FileProviders.Physical": "3.0.0", - "Microsoft.Extensions.Logging.Configuration": "3.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "3.0.0", - "System.Threading.Channels": "4.7.1" + "Microsoft.CodeAnalysis.Analyzers": "3.3.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Metadata": "6.0.1", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Text.Encoding.CodePages": "6.0.0" } }, - "Macross.Json.Extensions": { + "Microsoft.CodeAnalysis.CSharp": { "type": "Transitive", - "resolved": "3.0.0", - "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" + "resolved": "4.5.0", + "contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[4.5.0]" + } }, - "Microsoft.AspNet.WebApi.Client": { + "Microsoft.CodeAnalysis.CSharp.Workspaces": { "type": "Transitive", - "resolved": "5.2.9", - "contentHash": "cuVhPjjNMSEFpKXweMNBbsG4RUFuuZpFBm8tSyw309U9JEjcnbB6n3EPb4xwgcy9bJ38ctIbv5G8zXUBhlrPWw==", + "resolved": "4.5.0", + "contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==", "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp": "[4.5.0]", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]" } }, - "Microsoft.Bcl.AsyncInterfaces": { + "Microsoft.CodeAnalysis.Workspaces.Common": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + "resolved": "4.5.0", + "contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.CodeAnalysis.Common": "[4.5.0]", + "System.Composition": "6.0.0", + "System.IO.Pipelines": "6.0.3", + "System.Threading.Channels": "6.0.0" + } }, - "Microsoft.CodeCoverage": { + "Microsoft.Bcl.HashCode": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "2oZbSVTC2nAvQ2DnbXLlXS+c25ZyZdWeNd+znWwAxwGaPh9dwQ5NBsYyqQB7sKmJKIUdkKGmN3rzFzjVC81Dtg==" + "resolved": "1.1.1", + "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" }, - "Microsoft.CSharp": { + "Microsoft.CodeAnalysis.Analyzers": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==" + "resolved": "17.13.0", + "contentHash": "9LIUy0y+DvUmEPtbRDw6Bay3rzwqFV8P4efTrK4CZhQle3M/QwLPjISghfcolmEGAPWxuJi6m98ZEfk4VR4Lfg==" }, "Microsoft.Data.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xCcaePISVs3Fdy+ji1yGDp1gCjUwDJpfIKrBWXWDgyzc3R2MmNxTW5YgNmnB7dvdHoJwf0jPZ50M5TBj7noV3w==", + "resolved": "8.0.14", + "contentHash": "MT/9fCazlL4T10BwCQCxvUXOmtU4rR1qDl2mpePFhmuXONafUjXUf8FH94IR79ISxrGVHxsOWvwGzgKi6RSE/g==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Microsoft.EntityFrameworkCore.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "KJCJjFMZFGYy0G8a8ZUwAe9n/l6P+dP3i4fQJmR4jR0/EFnlfeNeWh8n6nRhP+9YmNz290twaIZSbRoiGU6S2A==" + "resolved": "8.0.14", + "contentHash": "Om8/jdWyx9eKFkA1YEgxk13KjGIzA8teLgG7iNFunsI2+MT6UT54Eb4t6oe4NQlIaACj5voUe6szVAQe9GKwDA==" }, "Microsoft.EntityFrameworkCore.Analyzers": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "xke0hphu+BSBwt6Kfv/XERe3s1G7BZjNUByyNj0oIZVD1KPaIhMQJBKHtblkCI04cMnO1Ac2NMEgO67rM+cP/w==" + "resolved": "8.0.14", + "contentHash": "lzNb3s4t5JDMHGoUFuX/f977dFythvmzGFJxvjlhExdiATPKQfquo2NM0uX8Kelfq04jRljpdbRzcsSsK1q9Tw==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "ncCvbJYGXK7eSOVqfQNXLaxMWKGaKSYX1VJZjyJXg3IxxmF50B8p/isprgrVLR+SlQwTG1lmhPAPn0dOvCqlrw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2", + "Mono.TextTemplating": "2.2.1" + } }, "Microsoft.EntityFrameworkCore.Relational": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "cB1n/Hj8HLYuyIE6fEZyaAKn5qdU9QpDtFZ3KNLWyiZfftmY2T7Bz1Aea1DIUM/KQF22URRLkj7bs4S6CIEp+w==", + "resolved": "8.0.14", + "contentHash": "cPEeIk9nFO3+hxj9tp5AvTFdcTZkVPJCOFUiagbf37KhPGtiG0ZWpl15xOzLYTDAYjF5kxH/jcuDYGlLACJEmA==", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.EntityFrameworkCore": "8.0.14", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" } }, "Microsoft.EntityFrameworkCore.Sqlite.Core": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "hV7yq12omAd1ccKCfMJS9xsz7+FxQeSGqRdWIIyWaUXmwmK9Df644mBpj0SDMORjmhsNz9L7EqwbZW+iyQi0VQ==", + "resolved": "8.0.14", + "contentHash": "TcbHy/SdKTcrxlgx14uicVMqrBTu3SP3STGicR+JzYG4I3mVtsBgqtArt6mmUtA7UZj7sogXJ6EFpSyNsJU8Zg==", "dependencies": { - "Microsoft.Data.Sqlite.Core": "6.0.11", - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.DependencyModel": "6.0.0" + "Microsoft.Data.Sqlite.Core": "8.0.14", + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.DependencyModel": "8.0.2" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Transitive", + "resolved": "8.0.14", + "contentHash": "nWlJaFEvT7HpLkOusyReeQPeHsl5EUIBlI5Pr05SYhkWFQ7KGLmUNdv4j1aAjrO+x42Enqr7fu7r3MXLPQqImg==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Design": "8.0.14" } }, "Microsoft.Extensions.ApiDescription.Server": { @@ -397,305 +463,236 @@ }, "Microsoft.Extensions.Caching.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bcz5sSFJbganH0+YrfvIjJDIcKNW7TL07C4d1eTmXy/wOt52iz4LVogJb6pazs7W0+74j0YpXFErvp++Aq5Bsw==", + "resolved": "8.0.0", + "contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Caching.Memory": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "B4y+Cev05eMcjf1na0v9gza6GUtahXbtY1JCypIgx3B4Ea/KAgsWyXEmW4q6zMbmTMtKzmPVk09rvFJirvMwTg==", + "resolved": "8.0.1", + "contentHash": "HFDnhYLccngrzyGgHkjEDU5FMLn4MpOsr5ElgsBMC4yx6lJh4jeWO7fHS8+TXPq+dgxCmUa/Trl8svObmwW4QA==", "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Caching.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "qWzV9o+ZRWq+pGm+1dF+R7qTgTYoXvbyowRoBxQJGfqTpqDun2eteerjRQhq5PQ/14S+lqto3Ft4gYaRyl4rdQ==", - "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.CommandLine": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "3nL1qCkZ1Oxx14ZTzgo4MmlO7tso7F+TtMZAY2jUAtTLyAcDp+EDjk3RqafoKiNaePyPvvlleEcBxh3b2Hzl1g==", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.Configuration.FileExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "V4Dth2cYMZpw3HhGw9XUDIijpI6gN+22LDt0AhufIgOppCUfpWX4483OmN+dFXRJkJLc8Tv0Q8QK+1ingT2+KQ==", + "resolved": "8.0.1", + "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" - } - }, - "Microsoft.Extensions.Configuration.UserSecrets": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "Fy8yr4V6obi7ZxvKYI1i85jqtwMq8tqyxQVZpRSkgeA8enqy/KvBIMdcuNdznlxQMZa72mvbHqb7vbg4Pyx95w==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Physical": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "vWXPg3HJQIpZkENn1KWq8SfbqVujVD7S7vIAyFXXqK5xkf1Vho+vG0bLBCHxU36lD1cLLtmGpfYf0B3MYFi9tQ==", + "resolved": "8.0.1", + "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" }, "Microsoft.Extensions.DependencyModel": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TD5QHg98m3+QhgEV1YVoNMl5KtBw/4rjfxLHO0e/YV9bPUBDKntApP4xdrVtGgCeQZHVfC2EXIGsdpRNrr87Pg==", + "resolved": "8.0.2", + "contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "elH2vmwNmsXuKmUeMQ4YW9ldXiF+gSGDgg1vORksob5POnpaI6caj1Hu8zaYbEuibhqCoWg0YRWDazBY3zjBfg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "E6HxKQvrm0AeDagW6w+CsyVfXAO/pscrbX6mQ+XnThdwkeTxi0cnuXDTiTmd+WSmofSfpBKOS0VlvHUOxskdLQ==", + "resolved": "8.0.14", + "contentHash": "obv82U5+okAtAP8K2Ne027Y8rfvseUPUNZUMVUffRB+Unom8mjzvqL/GzUx7rPj6f9e/hQbGwF5ya5RZq7327Q==", "dependencies": { - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "MQS7GE1ux7Lo1yOr59M7ZTEoFY3GJ9hHkxXQnQc8EPxkt5S7cX4qe6djSWH+mk9qQan+AjFZzdC1x5Af5IaseA==" + "resolved": "8.0.14", + "contentHash": "se5sdveMiA3PUOWchOZXY/sGA50MrJ/Mg/G6CdQBtyA4MLySNRilVCi23YT90RAwqvI2uQEk5+buxYdpAfuwpA==" }, "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": { "type": "Transitive", - "resolved": "6.0.11", - "contentHash": "tMjF1erFhHE+SnsiIyRIatVeKBgB9OfGsOvQe/+foE0xl4+JUQGbCA7gF1wqOksi9AxmGWzqtqjsMKJpCo5wYQ==", + "resolved": "8.0.14", + "contentHash": "4b/wu7E9oNd994GQyehsJkoLAC8BVrRkO6rzWuWTmHm0w0A5m4giPx35BWd7nJ5h0mq2Cfk0ueHlBQo/ICyfJA==", "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.11", - "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.11" + "Microsoft.EntityFrameworkCore.Relational": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.14" } }, "Microsoft.Extensions.FileProviders.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "0pd4/fho0gC12rQswaGQxbU34jOS1TPS8lZPpkFCH68ppQjHNHYle9iRuHeev1LhrJ94YPvzcRd8UmIuFk23Qw==", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileProviders.Physical": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "QvkL7l0nM8udt3gfyu0Vw8bbCXblxaKOl7c2oBfgGy4LCURRaL9XWZX1FWJrQc43oMokVneVxH38iz+bY1sbhg==", + "resolved": "8.0.0", + "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==", "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileSystemGlobbing": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.FileSystemGlobbing": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, "Microsoft.Extensions.FileSystemGlobbing": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ip8jnL1aPiaPeKINCqaTEbvBFDmVx9dXQEBZ2HOBRXPD1eabGNqP/bKlsIcp7U2lGxiXd5xIhoFcmY8nM4Hdiw==" - }, - "Microsoft.Extensions.Hosting": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "hbmizc9KPWOacLU8Z8YMaBG6KWdZFppczYV/KwnPGU/8xebWxQxdDeJmLOgg968prb7g2oQgnp6JVLX6lgby8g==", - "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.Configuration.CommandLine": "6.0.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Configuration.UserSecrets": "6.0.1", - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Physical": "6.0.0", - "Microsoft.Extensions.Hosting.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Logging.Debug": "6.0.0", - "Microsoft.Extensions.Logging.EventLog": "6.0.0", - "Microsoft.Extensions.Logging.EventSource": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ==" }, "Microsoft.Extensions.Hosting.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "6.0.0" - } - }, - "Microsoft.Extensions.Http": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "15+pa2G0bAMHbHewaQIdr/y6ag2H3yh4rd9hTXavtWDzQBkvpe2RMqFg8BxDpcQWssmjmBApGPcw93QRz6YcMg==", + "resolved": "8.0.1", + "contentHash": "nHwq9aPBdBPYXPti6wYEEfgXddfBrYC+CQLn+qISiwQq5tpfaqDZSKOJNxoe9rfQxGf1c+2wC/qWFe1QYJPYqw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2" } }, "Microsoft.Extensions.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "resolved": "8.0.1", + "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.0" + "Microsoft.Extensions.DependencyInjection": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "6.0.3", - "contentHash": "SUpStcdjeBbdKjPKe53hVVLkFjylX0yIXY8K+xWa47+o1d+REDyOMZjHZa+chsQI1K9qZeiHWk9jos0TFU7vGg==" + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "ZDskjagmBAbv+K8rYW9VhjPplhbOE63xUD0DiuydZJwt15dRyoqicYklLd86zzeintUc7AptDkHn+YhhYkYo8A==", + "resolved": "8.0.1", + "contentHash": "QWwTrsgOnJMmn+XUslm8D2H1n3PkP/u/v52FODtyBc/k4W9r3i2vcXXeeX/upnzllJYRRbrzVzT0OclfNJtBJA==", "dependencies": { - "Microsoft.Extensions.Configuration": "6.0.0", - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0" + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "8.0.0" } }, - "Microsoft.Extensions.Logging.Console": { + "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "gsqKzOEdsvq28QiXFxagmn1oRB9GeI5GgYCkoybZtQA0IUb7QPwf1WmN3AwJeNIsadTvIFQCiVK0OVIgKfOBGg==", + "resolved": "8.0.2", + "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging.Configuration": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.Debug": { + "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "M9g/JixseSZATJE9tcMn9uzoD4+DbSglivFqVx8YkRJ7VVPmnvCEbOZ0AAaxsL1EKyI4cz07DXOOJExxNsUOHw==", + "resolved": "8.0.0", + "contentHash": "0f4DMRqEd50zQh+UyJc+/HiBsZ3vhAQALgdkcQEalSH1L2isdC7Yj54M3cyo5e+BeO5fcBQ7Dxly8XiBBcvRgw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" } }, - "Microsoft.Extensions.Logging.EventLog": { + "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "rlo0RxlMd0WtLG3CHI0qOTp6fFn7MvQjlrCjucA31RqmiMFCZkF8CHNbe8O7tbBIyyoLGWB1he9CbaA5iyHthg==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "System.Diagnostics.EventLog": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" }, - "Microsoft.Extensions.Logging.EventSource": { + "Microsoft.IdentityModel.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "BeDyyqt7nkm/nr+Gdk+L8n1tUT/u33VkbXAOesgYSNsxDM9hJ1NOBGoZfj9rCbeD2+9myElI6JOVVFmnzgeWQA==", + "resolved": "7.1.2", + "contentHash": "33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "6.0.0" + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Options": { + "Microsoft.IdentityModel.Logging": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "resolved": "7.1.2", + "contentHash": "YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Abstractions": "7.1.2" } }, - "Microsoft.Extensions.Options.ConfigurationExtensions": { + "Microsoft.IdentityModel.Protocols": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "bXWINbTn0vC0FYc9GaQTISbxhQLAMrvtbuvD9N6JelEaIS/Pr62wUCinrq5bf1WRBGczt1v4wDhxFtVFNcMdUQ==", + "resolved": "7.1.2", + "contentHash": "SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Configuration.Binder": "6.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Microsoft.Extensions.Primitives": "6.0.0" + "Microsoft.IdentityModel.Logging": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" } }, - "Microsoft.Extensions.Primitives": { + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + "resolved": "7.1.2", + "contentHash": "6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "Microsoft.IdentityModel.Protocols": "7.1.2", + "System.IdentityModel.Tokens.Jwt": "7.1.2" } }, - "Microsoft.Net.Http.Headers": { + "Microsoft.IdentityModel.Tokens": { "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "wHdwMv0QDDG2NWDSwax9cjkeQceGC1Qq53a31+31XpvTXVljKXRjWISlMoS/wZYKiqdqzuEvKFKwGHl+mt2jCA==", + "resolved": "7.1.2", + "contentHash": "oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==", "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" + "Microsoft.IdentityModel.Logging": "7.1.2" } }, "Microsoft.NETCore.Platforms": { @@ -705,37 +702,31 @@ }, "Microsoft.NETCore.Targets": { "type": "Transitive", - "resolved": "1.1.3", - "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "1.2.3", - "contentHash": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==" + "resolved": "1.6.23", + "contentHash": "tZ1I0KXnn98CWuV8cpI247A17jaY+ILS9vvF7yhI0uPPEqF4P1d7BWL5Uwtel10w9NucllHB3nTkfYTAcHAh8g==" }, "Microsoft.TestPlatform.ObjectModel": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "oWe7A0wrZhxagTOcaxJ9r0NXTbgkiBQQuCpCXxnP06NsGV/qOoaY2oaangAJbOUrwEx0eka1do400NwNCjfytw==", + "resolved": "17.13.0", + "contentHash": "bt0E0Dx+iqW97o4A59RCmUmz/5NarJ7LRL+jXbSHod72ibL5XdNm1Ke+UO5tFhBG4VwHLcSjqq9BUSblGNWamw==", "dependencies": { - "NuGet.Frameworks": "5.11.0", "System.Reflection.Metadata": "1.6.0" } }, "Microsoft.TestPlatform.TestHost": { "type": "Transitive", - "resolved": "17.4.0", - "contentHash": "sUx48fu9wgQF1JxzXeSVtzb7KoKpJrdtIzsFamxET3ZYOKXj+Ej13HWZ0U2nuMVZtZVHBmE+KS3Vv5cIdTlycQ==", + "resolved": "17.13.0", + "contentHash": "9GGw08Dc3AXspjekdyTdZ/wYWFlxbgcF0s7BKxzVX+hzAwpifDOdxM+ceVaaJSQOwqt3jtuNlHn3XTpKUS9x9Q==", "dependencies": { - "Microsoft.TestPlatform.ObjectModel": "17.4.0", + "Microsoft.TestPlatform.ObjectModel": "17.13.0", "Newtonsoft.Json": "13.0.1" } }, - "Microsoft.Toolkit.HighPerformance": { - "type": "Transitive", - "resolved": "7.1.2", - "contentHash": "cezzRky0BUJyYmSrcQUcX8qAv90JfUwCqWEbqfWZLHyeANo9/LWgW6y50pqbyc8r8SPXVsu2GNH98fB3VxrnvA==" - }, "Microsoft.Win32.Registry": { "type": "Transitive", "resolved": "5.0.0", @@ -747,129 +738,130 @@ }, "Monai.Deploy.Messaging": { "type": "Transitive", - "resolved": "0.1.16", - "contentHash": "k8PwzNCgovENqZnA6Uh/TjADd2LadFSWs88b0LCDTGsxq7hkRTIqGLzp6aqw9e8LGNff6WW7dtVGj31PuceKmQ==", + "resolved": "2.0.4", + "contentHash": "eehOa4v9i4uq4QxFFZ2yj5q9/R0euMCMSlKsY/lpHwsInOi63etUA3lEqikdul6PTCyRRYh+ubFqmW03PDFQfg==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Newtonsoft.Json": "13.0.1", - "System.ComponentModel.Annotations": "5.0.0", - "System.IO.Abstractions": "17.2.3" + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Newtonsoft.Json": "13.0.3", + "System.IO.Abstractions": "21.3.1" + } + }, + "Monai.Deploy.Security": { + "type": "Transitive", + "resolved": "1.0.1", + "contentHash": "GrGj/addv+V5MgZuHBXV68M9PtyzrPlvHibyaHPQq602wtK4CYqPGR5P4VJgM2ZuiW89vEKbhd07sxTFkG9Muw==", + "dependencies": { + "Ardalis.GuardClauses": "4.6.0", + "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.14", + "Microsoft.Extensions.Configuration": "8.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "Microsoft.Extensions.Logging.Configuration": "8.0.1" } }, "Monai.Deploy.Storage": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "qTk/hYUIA1XCohRxG2XcFqoI3gzZTgPyB/DbRyeY4nVZ7lmuzni+KrbHuewTqsmBKt00+2d9YI6gms5oMcTxsQ==", + "resolved": "1.0.2", + "contentHash": "pWhQfV2QzdirCV0J7kDsMpnAKzSUb+uaWeQxgD+BqtFbrlX8RFFrIIDkOfZlMbAhJMuwbTNVqE/ZLtWP4fuSjA==", "dependencies": { - "AWSSDK.SecurityToken": "3.7.100.6", - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10", - "Microsoft.Extensions.Logging": "6.0.0", - "Monai.Deploy.Storage.S3Policy": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "AWSSDK.SecurityToken": "3.7.401.68", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.14", + "Monai.Deploy.Storage.S3Policy": "1.0.2", + "System.IO.Abstractions": "21.3.1" } }, "Monai.Deploy.Storage.S3Policy": { "type": "Transitive", - "resolved": "0.2.10", - "contentHash": "uCv90cT8z0qxlLo2Y/biem6vY9+nrD0EJBHTYETYooXp1tnAAt77pvvLx4ygFFzoabUTjdMr9ptYSJOXQ4dAFQ==", + "resolved": "1.0.2", + "contentHash": "SDQb0HmTV99ysIT2WfHkUzWPNRtDpnuES358F3wNaQ6VZ6lSCaaSlzJpdsbf1kc0OKjiXntj1D3u63finvlHrw==", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Newtonsoft.Json": "13.0.1" + "Ardalis.GuardClauses": "4.6.0", + "Newtonsoft.Json": "13.0.3" } }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "iyiVjkCAZIUiyYDZXXUqISeW7n3O/qcM90PUeJybryg7g4rXhSMRY0oLpAg+NdoXD/Qm9LlmVIePAluHQB91tQ==", + "resolved": "2.30.0", + "contentHash": "Gg0TQUT3IEntcqdug5a9P6d8iwL5CqOpQjVBCq1hxTbkjxdGdY6a2CPv7II44AO9GYUnORYsS6dDME2b7aqYyg==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "MongoDB.Driver": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "nq7wRMeNoqUe+bndHFMDGX8IY3iSmzLoyLzzf8DRos137O+5R4NCsd9qtw/n+DoGFas0gzzyD546Cpz+5AkmLg==", + "resolved": "2.30.0", + "contentHash": "BCG8cNF0+U3h5f/O9fu3ktrYhoESBDems1w06PExfYrn2KjHBHCBdvBRY1cIbysnZVjQAJjGtFV9XgW+hXt7Hg==", "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Driver.Core": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0" + "MongoDB.Bson": "2.30.0", + "MongoDB.Driver.Core": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0" } }, "MongoDB.Driver.Core": { "type": "Transitive", - "resolved": "2.18.0", - "contentHash": "/X5Ty32gyDyzs/fWFwKGS0QUhfQT3V9Sc/F8yhILBu8bjCjBscOFKQsKieAha8xxBnYS7dZvTvhvEJWT7HgJ1g==", + "resolved": "2.30.0", + "contentHash": "oepDgu24lo44SljuHmIQ99x6jHISnMC4tLfzQGniQg39xiMD8nxalm1HM9RDZcuZbbWa4F6YLt2AIhWkny3XWA==", "dependencies": { + "AWSSDK.SecurityToken": "3.7.100.14", "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "2.18.0", - "MongoDB.Libmongocrypt": "1.6.0", + "MongoDB.Bson": "2.30.0", + "MongoDB.Libmongocrypt": "1.12.0", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", - "ZstdSharp.Port": "0.6.2" + "ZstdSharp.Port": "0.7.3" } }, "MongoDB.Libmongocrypt": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "kh+MMf+ECIf5sQDIqOdKBd75ktD5aD1EuzCX3R4HOUGPlAbeAm8harf4zwlbvFe2BLfCXZO7HajSABLf4P0GNg==" + "resolved": "1.12.0", + "contentHash": "B1X51jrtNacKvxKoaqWeknYeJfQS5aWf6BmVLT5JZerz3AUXFzv8edPskJYqBc3kLy1J2PWzMqqsnyb9g8FtcA==" }, - "NETStandard.Library": { + "Mono.TextTemplating": { "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==", + "resolved": "2.2.1", + "contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" + "System.CodeDom": "4.4.0" } }, "Newtonsoft.Json": { "type": "Transitive", - "resolved": "13.0.1", - "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" - }, - "Newtonsoft.Json.Bson": { - "type": "Transitive", - "resolved": "1.0.1", - "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - } + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, "NLog": { "type": "Transitive", - "resolved": "5.0.5", - "contentHash": "NOTWSyUEljmjMq7OqZ1X9iu4bJ+rW/o6pt79Jq8j2Q7s8DyoMMCJwe0HoCKcNjhYRJ++b+E8erH6E6WwaCTshQ==" + "resolved": "5.4.0", + "contentHash": "LwMcGSW3soF3/SL68rlJN3Eh3ktrAPycC3zZR/07OYBPraZUu0bygEC7kIN10lUQgMXT4s84Fi1chglGdGrQEg==" }, "NLog.Extensions.Logging": { "type": "Transitive", - "resolved": "5.1.0", - "contentHash": "cQCKF2/iYjZUkn0d2o6VD1xkTUhIFHPYmZEm29KlTthLEzMht5aY80SwWlHZCKy0w19kaSq1jgLJSGrKsapUfg==", + "resolved": "5.4.0", + "contentHash": "5T19INfbzRywZpyBGoQChsB/R7eHW9hR4Ml9O22NRjJpfltGIJ+rsoCcjwr2/V/E6i3/eXLTQwRAeDMICjWpTA==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "6.0.0", - "Microsoft.Extensions.Logging": "6.0.0", - "NLog": "5.0.5" + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "NLog": "5.4.0" } }, "NLog.Web.AspNetCore": { "type": "Transitive", - "resolved": "5.1.5", - "contentHash": "a7Pe6KdwIxPxcFYy6M7wLseU++tx1bBf/ROVNlcZPLfp40DPLA0KGOk1K9kvbcwPYKKLMikdSwiOyTRZZFlaXg==", + "resolved": "5.4.0", + "contentHash": "Le6rbipSqeGVygbLGgRwZNMsbjJ+YnoAMJdZVhgjQimXwmYbDNkSswK6Mb32bKoC5aIg5mhw+hpAmgWdegs4Eg==", "dependencies": { - "NLog.Extensions.Logging": "5.1.0" + "NLog.Extensions.Logging": "5.4.0" } }, - "NuGet.Frameworks": { + "Polly.Core": { "type": "Transitive", - "resolved": "5.11.0", - "contentHash": "eaiXkUjC4NPcquGWzAGMXjuxvLwc6XGKMptSyOGQeT0X70BUZObuybJFZLA0OfTdueLd3US23NBPTBb6iF3V1Q==" + "resolved": "8.5.2", + "contentHash": "1MJKdxv4zwDmiWvYvVN24DsrWUfgQ4F83voH8bhbtLMdPuGy8CfTUzsgQhvyrl1a7hrM6f/ydwLVdVUI0xooUw==" }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { "type": "Transitive", @@ -1008,72 +1000,84 @@ }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "zssYqiaucyGArZfg74rJuzK0ewgZiidsRVrZTmP7JLNvK806gXg6PGA46XzoJGpNPPA5uRcumwvVp6YTYxtQ5w==", + "resolved": "2.1.6", + "contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6", - "SQLitePCLRaw.lib.e_sqlite3": "2.0.6", - "SQLitePCLRaw.provider.e_sqlite3": "2.0.6" + "SQLitePCLRaw.lib.e_sqlite3": "2.1.6", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.6" } }, "SQLitePCLRaw.core": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "Vh8n0dTvwXkCGur2WqQTITvk4BUO8i8h9ucSx3wwuaej3s2S6ZC0R7vqCTf9TfS/I4QkXO6g3W2YQIRFkOcijA==", + "resolved": "2.1.6", + "contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==", "dependencies": { "System.Memory": "4.5.3" } }, "SQLitePCLRaw.lib.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "xlstskMKalKQl0H2uLNe0viBM6fvAGLWqKZUQ3twX5y1tSOZKe0+EbXopQKYdbjJytNGI6y5WSKjpI+kVr2Ckg==" + "resolved": "2.1.6", + "contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q==" }, "SQLitePCLRaw.provider.e_sqlite3": { "type": "Transitive", - "resolved": "2.0.6", - "contentHash": "peXLJbhU+0clVBIPirihM1NoTBqw8ouBpcUsVMlcZ4k6fcL2hwgkctVB2Nt5VsbnOJcPspQL5xQK7QvLpxkMgg==", + "resolved": "2.1.6", + "contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==", "dependencies": { - "SQLitePCLRaw.core": "2.0.6" + "SQLitePCLRaw.core": "2.1.6" } }, "Swashbuckle.AspNetCore": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "eUBr4TW0up6oKDA5Xwkul289uqSMgY0xGN4pnbOIBqCcN9VKGGaPvHX3vWaG/hvocfGDP+MGzMA0bBBKz2fkmQ==", + "resolved": "8.0.0", + "contentHash": "K9FzGTxmwfD+7sVf/FTq/TZFHBTXcROgdcg7gLFwKwgvXwaqTtjGVdam27j0kYfgZZyWlOKr+abmtyd2nAd5eA==", "dependencies": { "Microsoft.Extensions.ApiDescription.Server": "6.0.5", - "Swashbuckle.AspNetCore.Swagger": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerGen": "6.4.0", - "Swashbuckle.AspNetCore.SwaggerUI": "6.4.0" + "Swashbuckle.AspNetCore.Swagger": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerGen": "8.0.0", + "Swashbuckle.AspNetCore.SwaggerUI": "8.0.0" } }, "Swashbuckle.AspNetCore.Swagger": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "nl4SBgGM+cmthUcpwO/w1lUjevdDHAqRvfUoe4Xp/Uvuzt9mzGUwyFCqa3ODBAcZYBiFoKvrYwz0rabslJvSmQ==", + "resolved": "8.0.0", + "contentHash": "+8Y4pVTWbnzotIk6d6rcwsHGpCchPDqqrvYkyGlI3go+pFaKM+4eX30iCyI0hvr0RMtObJCFhK6aDtlQFbEF1g==", "dependencies": { - "Microsoft.OpenApi": "1.2.3" + "Microsoft.OpenApi": "1.6.23" } }, "Swashbuckle.AspNetCore.SwaggerGen": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "lXhcUBVqKrPFAQF7e/ZeDfb5PMgE8n5t6L5B6/BQSpiwxgHzmBcx8Msu42zLYFTvR5PIqE9Q9lZvSQAcwCxJjw==", + "resolved": "8.0.0", + "contentHash": "skCeIQ93yMcUm1PQby5qitFM6KLIlLMj4/i8JHy86x2OFzxTNaaas2kUg6rNV3JvucFvYCNyImg7NMtZHErSzQ==", "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.4.0" + "Swashbuckle.AspNetCore.Swagger": "8.0.0" } }, "Swashbuckle.AspNetCore.SwaggerUI": { "type": "Transitive", - "resolved": "6.4.0", - "contentHash": "1Hh3atb3pi8c+v7n4/3N80Jj8RvLOXgWxzix6w3OZhB7zBGRwsy7FWr4e3hwgPweSBpwfElqj4V4nkjYabH9nQ==" + "resolved": "8.0.0", + "contentHash": "IMqmgclFiZL2QIfopOmWYofZzckrl+SdMt1h4mKC0jc94F+uzt3IHA3YFC0CGlwBqTTSnxHqNUKomNTeAhZbYA==" + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==" + }, "System.Collections": { "type": "Transitive", "resolved": "4.3.0", @@ -1109,10 +1113,53 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.ComponentModel.Annotations": { + "System.Composition": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg==" + "resolved": "6.0.0", + "contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Convention": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0", + "System.Composition.TypedParts": "6.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==", + "dependencies": { + "System.Composition.Runtime": "6.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==", + "dependencies": { + "System.Composition.AttributedModel": "6.0.0", + "System.Composition.Hosting": "6.0.0", + "System.Composition.Runtime": "6.0.0" + } }, "System.Configuration.ConfigurationManager": { "type": "Transitive", @@ -1135,10 +1182,14 @@ }, "System.Diagnostics.DiagnosticSource": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "resolved": "4.3.0", + "contentHash": "tD6kosZnTAGdrEa0tZSuFyunMbt/5KYDnHdndJYGqZoNy00XVXyACd5d6KnE1YgYv3ne2CjtAfNXo/fwEhnKUA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" + "System.Collections": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" } }, "System.Diagnostics.EventLog": { @@ -1190,6 +1241,15 @@ "System.Runtime.InteropServices": "4.3.0" } }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.1.2", + "contentHash": "Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.1.2", + "Microsoft.IdentityModel.Tokens": "7.1.2" + } + }, "System.IO": { "type": "Transitive", "resolved": "4.3.0", @@ -1204,8 +1264,21 @@ }, "System.IO.Abstractions": { "type": "Transitive", - "resolved": "17.2.3", - "contentHash": "VcozGeE4SxIo0cnXrDHhbrh/Gb8KQnZ3BvMelvh+iw0PrIKtuuA46U2Xm4e4pgnaWFgT4RdZfTpWl/WPRdw0WQ==" + "resolved": "21.3.1", + "contentHash": "Gm8HI/AHwoWd1r9IUShekWgAQjJgTM1jmrJHSkxONeuVUQAZdxSKzGYTjReBYgqLvF1Zq1Hcd1qHytrL0HuiBg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1", + "TestableIO.System.IO.Abstractions.Wrappers": "21.3.1" + } }, "System.IO.FileSystem": { "type": "Transitive", @@ -1230,6 +1303,16 @@ "System.Runtime": "4.3.0" } }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ne1843evDugl0md7Fjzy6QjJrzsjh46ZKbhf8GwBXb5f/gw97J4bxMs0NQKifDuThh/f0bZ0e62NPl1jzTuRqA==" + }, + "System.IO.Pipelines": { + "type": "Transitive", + "resolved": "6.0.3", + "contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==" + }, "System.Linq": { "type": "Transitive", "resolved": "4.3.0", @@ -1244,8 +1327,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Net.Http": { "type": "Transitive", @@ -1282,28 +1365,19 @@ }, "System.Net.Primitives": { "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "OHzPhSme78BbmLe9UBxHM69ZYjClS5URuhce6Ta4ikiLgaUGiG/X84fZpI6zy7CsUH5R9cYzI2tv9dWPqdTkUg==", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "System.Runtime": "4.3.1", + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", "System.Runtime.Handles": "4.3.0" } }, "System.Reactive": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==" - }, - "System.Reactive.Linq": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "IB4/qlV4T1WhZvM11RVoFUSZXPow9VWVeQ1uDkSKgz6bAO+gCf65H/vjrYlwyXmojSSxvfHndF9qdH43P/IuAw==", - "dependencies": { - "System.Reactive": "5.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } + "resolved": "6.0.0", + "contentHash": "31kfaW4ZupZzPsI5PVe77VhnvFF55qgma7KZr/E0iFTs6fmdhhG8j0mgEx620iLTey1EynOkEfnyTjtNEpJzGw==" }, "System.Reflection": { "type": "Transitive", @@ -1319,8 +1393,11 @@ }, "System.Reflection.Metadata": { "type": "Transitive", - "resolved": "1.6.0", - "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + "resolved": "6.0.1", + "contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==", + "dependencies": { + "System.Collections.Immutable": "6.0.0" + } }, "System.Reflection.Primitives": { "type": "Transitive", @@ -1346,11 +1423,11 @@ }, "System.Runtime": { "type": "Transitive", - "resolved": "4.3.1", - "contentHash": "abhfv1dTK6NXOmu4bgHIONxHyEqFjW8HwXPmpY9gmll+ix9UNo4XDcmzJn6oLooftxNssVHdJC1pGT9jkSynQg==", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3" + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" } }, "System.Runtime.CompilerServices.Unsafe": { @@ -1595,27 +1672,25 @@ }, "System.Text.Encoding.CodePages": { "type": "Transitive", - "resolved": "4.6.0", - "contentHash": "OCUK9C/U97+UheVwo+JE+IUcKySUE3Oe+BcHhVtQrvmKSUFLrUDO8B5zEPRL6mBGbczxZp4w1boSck6/fw4dog==", - "dependencies": { - "Microsoft.NETCore.Platforms": "3.0.0" - } + "resolved": "6.0.1", + "contentHash": "OV04vEWTSDXzaAJCjylOIdjB7Z7QTYQcz4/ATZSiG8PLkZLsbtaADj0Ydj4FdFnqq4PAwEA7SuILE+6ka4cn6A==" }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.7", - "contentHash": "/Tf/9XjprpHolbcDOrxsKVYy/mUG/FS7aGd9YUgBVEiHeQH4kAE0T1sMbde7q6B5xcrNUsJ5iW7D1RvHudQNqA==", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" + "System.Runtime": "4.3.0" } }, "System.Threading": { @@ -1629,8 +1704,8 @@ }, "System.Threading.Channels": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==" + "resolved": "8.0.0", + "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==" }, "System.Threading.Tasks": { "type": "Transitive", @@ -1642,20 +1717,18 @@ "System.Runtime": "4.3.0" } }, - "System.Threading.Tasks.Dataflow": { + "TestableIO.System.IO.Abstractions": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA==" + "resolved": "21.3.1", + "contentHash": "B9USlBOZAiqXss7AI4BH6HVWs+HoHx38OadJjBO0VCzEWgP/u0u52bogmrzDHsyqRv8Yo/xtIMQXgpjLoaAUXw==" }, - "System.Threading.Tasks.Extensions": { + "TestableIO.System.IO.Abstractions.Wrappers": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "BahUww/+mdP4ARCAh2RQhQTg13wYLVrBb9SYVgW8ZlrwjraGCXHGjo0oIiUfZ34LUZkMMR+RAzR7dEY4S1HeQQ==" + "resolved": "21.3.1", + "contentHash": "l/xu8G96pntsofFG8vh6BKbVbYWtqYZTpNCcj4jGNwxwSbwY2gvDmkiFmIbWf7lgzPZbopW2FAfaY6m4K/3QJw==", + "dependencies": { + "TestableIO.System.IO.Abstractions": "21.3.1" + } }, "Validation": { "type": "Transitive", @@ -1669,42 +1742,37 @@ }, "xunit.analyzers": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "BeO8hEgs/c8Ls2647fPfieMngncvf0D0xYNDfIO59MolxtCtVjFRd6SRc+7tj8VMqkVOuJcnc9eh4ngI2cAmLQ==" + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" }, "xunit.assert": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "pxJISOFjn2XTTi1mcDCkRZrTFb9OtRRCtx2kZFNF51GdReLr1ls2rnyxvAS4JO247K3aNtflvh5Q0346K5BROA==", - "dependencies": { - "NETStandard.Library": "1.6.1" - } + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" }, "xunit.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "KB4yGCxNqIVyekhJLXtKSEq6BaXVp/JO3mbGVE1hxypZTLEe7h+sTbAhpA+yZW2dPtXTuiW+C1B2oxxHEkrmOw==", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", "dependencies": { - "xunit.extensibility.core": "[2.4.2]", - "xunit.extensibility.execution": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" } }, "xunit.extensibility.core": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "W1BoXTIN1C6kpVSMw25huSet25ky6IAQUNovu3zGOGN/jWnbgSoTyCrlIhmXSg0tH5nEf8q7h3OjNHOjyu5PfA==", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", "dependencies": { - "NETStandard.Library": "1.6.1", "xunit.abstractions": "2.0.3" } }, "xunit.extensibility.execution": { "type": "Transitive", - "resolved": "2.4.2", - "contentHash": "CZmgcKkwpyo8FlupZdWpJCryrAOWLh1FBPG6gmVZuPQkGQsim/oL4PcP4nfrC2hHgXUFtluvaJ0Sp9PQKUMNpg==", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", "dependencies": { - "NETStandard.Library": "1.6.1", - "xunit.extensibility.core": "[2.4.2]" + "xunit.extensibility.core": "[2.8.1]" } }, "Xunit.SkippableFact": { @@ -1718,142 +1786,163 @@ }, "ZstdSharp.Port": { "type": "Transitive", - "resolved": "0.6.2", - "contentHash": "jPao/LdUNLUz8rn3H1D8W7wQbZsRZM0iayvWI4xGejJg3XJHT56gcmYdgmCGPdJF1UEBqUjucCRrFB+4HbJsbw==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "monai-deploy-informatics-gateway-pseudonymisation": { + "type": "Project", + "dependencies": { + "AnswerDicomTools": "[0.1.1-rc0089, )", + "Ardalis.GuardClauses": "[4.1.1, )", + "HL7-dotnetcore": "[2.36.0, )", + "Microsoft.EntityFrameworkCore": "[6.0.25, )", + "Microsoft.EntityFrameworkCore.Relational": "[6.0.25, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[6.0.25, )", + "Microsoft.Extensions.Configuration": "[6.0.1, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[6.0.0, )", + "Microsoft.Extensions.Configuration.Json": "[6.0.0, )", + "Microsoft.Extensions.Hosting": "[6.0.1, )", + "MongoDB.Driver": "[2.21.0, )", + "NLog": "[5.2.4, )", + "Polly": "[7.2.4, )", + "fo-dicom": "[5.1.1, )" + } }, "monai.deploy.informaticsgateway": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "DotNext.Threading": "4.7.4", - "HL7-dotnetcore": "2.29.0", - "Karambolo.Extensions.Logging.File": "3.3.1", - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", - "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Hosting": "6.0.1", - "Microsoft.Extensions.Logging": "6.0.0", - "Microsoft.Extensions.Logging.Console": "6.0.0", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "1.0.0", - "Monai.Deploy.Messaging.RabbitMQ": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "Monai.Deploy.Storage.MinIO": "0.2.10", - "NLog": "5.0.5", - "NLog.Web.AspNetCore": "5.1.5", - "Polly": "7.2.3", - "Swashbuckle.AspNetCore": "6.4.0", - "fo-dicom": "5.0.3", - "fo-dicom.NLog": "5.0.3" + "DotNext.Threading": "[5.5.0, )", + "HL7-dotnetcore": "[2.39.1, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.DicomWeb.Client": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.PlugIns.RemoteAppExecution": "[1.0.0, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Security": "[1.0.1, )", + "Monai.Deploy.Storage.MinIO": "[1.0.2, )", + "NLog.Web.AspNetCore": "[5.4.0, )", + "Swashbuckle.AspNetCore": "[8.0.0, )" } }, "monai.deploy.informaticsgateway.api": { "type": "Project", "dependencies": { - "Macross.Json.Extensions": "3.0.0", - "Microsoft.EntityFrameworkCore.Abstractions": "6.0.11", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10" + "HL7-dotnetcore": "[2.39.1, )", + "Macross.Json.Extensions": "[3.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[8.0.14, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )", + "Monai.Deploy.Messaging": "[2.0.4, )", + "Monai.Deploy.Messaging.RabbitMQ": "[2.0.4, )", + "Monai.Deploy.Storage": "[1.0.2, )", + "fo-dicom": "[5.2.1, )" } }, "monai.deploy.informaticsgateway.client": { "type": "Project", "dependencies": { - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.client.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.Text.Json": "6.0.7" + "Ardalis.GuardClauses": "[4.6.0, )" } }, "monai.deploy.informaticsgateway.common": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "System.IO.Abstractions": "17.2.3", - "System.Threading.Tasks.Dataflow": "6.0.0", - "fo-dicom": "5.0.3" + "Ardalis.GuardClauses": "[4.6.0, )", + "System.IO.Abstractions": "[21.3.1, )" } }, "monai.deploy.informaticsgateway.configuration": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "6.0.3", - "Microsoft.Extensions.Options": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Common": "1.0.0", - "Monai.Deploy.Messaging": "0.1.16", - "Monai.Deploy.Storage": "0.2.10", - "System.IO.Abstractions": "17.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Common": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.MongoDB": "1.0.0" + "AspNetCore.HealthChecks.MongoDb": "[8.1.0, )", + "Microsoft.EntityFrameworkCore.Tools": "[8.0.14, )", + "Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.EntityFramework": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.MongoDB": "[1.0.0, )" } }, "monai.deploy.informaticsgateway.database.api": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Polly": "7.2.3" + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "NLog": "[5.4.0, )" } }, "monai.deploy.informaticsgateway.database.entityframework": { "type": "Project", "dependencies": { - "Microsoft.EntityFrameworkCore": "6.0.11", - "Microsoft.EntityFrameworkCore.Sqlite": "6.0.11", - "Microsoft.Extensions.Configuration": "6.0.1", - "Microsoft.Extensions.Configuration.FileExtensions": "6.0.0", - "Microsoft.Extensions.Configuration.Json": "6.0.0", - "Monai.Deploy.InformaticsGateway.Api": "1.0.0", - "Monai.Deploy.InformaticsGateway.Configuration": "1.0.0", - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0" + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.database.mongodb": { "type": "Project", "dependencies": { - "Monai.Deploy.InformaticsGateway.Database.Api": "1.0.0", - "MongoDB.Driver": "2.18.0", - "MongoDB.Driver.Core": "2.18.0" + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "Polly": "[8.5.2, )" } }, "monai.deploy.informaticsgateway.dicomweb.client": { "type": "Project", "dependencies": { - "Ardalis.GuardClauses": "4.0.1", - "Microsoft.AspNet.WebApi.Client": "5.2.9", - "Microsoft.Extensions.Http": "6.0.0", - "Microsoft.Net.Http.Headers": "2.2.8", - "Monai.Deploy.InformaticsGateway.Client.Common": "1.0.0", - "System.Linq.Async": "6.0.1", - "fo-dicom": "5.0.3" + "Monai.Deploy.InformaticsGateway.Client.Common": "[1.0.0, )", + "fo-dicom": "[5.2.1, )" + } + }, + "monai.deploy.informaticsgateway.plugins.remoteappexecution": { + "type": "Project", + "dependencies": { + "Microsoft.EntityFrameworkCore": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Design": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Relational": "[8.0.14, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[8.0.14, )", + "Microsoft.Extensions.Configuration": "[8.0.0, )", + "Microsoft.Extensions.Configuration.FileExtensions": "[8.0.1, )", + "Microsoft.Extensions.Configuration.Json": "[8.0.1, )", + "Microsoft.Extensions.Options.ConfigurationExtensions": "[8.0.0, )", + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )", + "Monai.Deploy.InformaticsGateway.Configuration": "[1.0.0, )", + "Monai.Deploy.InformaticsGateway.Database.Api": "[1.0.0, )", + "MongoDB.Driver": "[2.30.0, )", + "NLog": "[5.4.0, )", + "Polly": "[8.5.2, )" + } + }, + "monai.deploy.informaticsgateway.test.plugins": { + "type": "Project", + "dependencies": { + "Monai.Deploy.InformaticsGateway.Api": "[0.4.1, )" } } } diff --git a/tests/Integration.Test/run.sh b/tests/Integration.Test/run.sh index 5bbbd9e79..08356d9cf 100755 --- a/tests/Integration.Test/run.sh +++ b/tests/Integration.Test/run.sh @@ -21,7 +21,7 @@ export SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" DOCKER_COMPOSE_DIR="$SCRIPT_DIR/../../docker-compose" TEST_DIR="$SCRIPT_DIR/" LOG_DIR="${GITHUB_WORKSPACE:-$SCRIPT_DIR}" -BIN_DIR="$TEST_DIR/bin/Release/net6.0" +BIN_DIR="$TEST_DIR/bin/Release/net8.0" FEATURE= export STUDYJSON="study.json" diff --git a/tests/Integration.Test/study.json b/tests/Integration.Test/study.json index c60b21406..cf0308fb7 100644 --- a/tests/Integration.Test/study.json +++ b/tests/Integration.Test/study.json @@ -10,8 +10,8 @@ "CT": { "SeriesMin": 1, "SeriesMax": 2, - "InstanceMin": 60, - "InstanceMax": 1000, + "InstanceMin": 50, + "InstanceMax": 300, "SizeMin": 0.5, "SizeMax": 1 }, @@ -55,4 +55,4 @@ "SizeMin": 1, "SizeMax": 2 } -} +} \ No newline at end of file