From d68e15d3f753a407f0cd1ed169b1180f674358b6 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 23 Nov 2023 04:52:02 +0100 Subject: [PATCH 01/45] Increment version to 5.5.1 (used for pre-release builds from ci) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index dbaf920..d13bf29 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,6 @@ false $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.5.0 + 5.5.1 From 605c2e810f1732806e63ae5e2afca1608e539d1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 02:01:22 +0000 Subject: [PATCH 02/45] Bump jetbrains.resharper.globaltools (#37) --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ad52741..4df279c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.0-eap08", + "version": "2023.3.0-rc01", "commands": [ "jb" ] @@ -21,4 +21,4 @@ ] } } -} +} \ No newline at end of file From ed2e5df045b65c1e668ebce68b355b33089f8e13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 02:51:35 +0000 Subject: [PATCH 03/45] Bump jetbrains.resharper.globaltools from 2023.3.0-rc01 to 2023.3.0 (#39) --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4df279c..81fbd25 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.0-rc01", + "version": "2023.3.0", "commands": [ "jb" ] @@ -21,4 +21,4 @@ ] } } -} \ No newline at end of file +} From 4ec3b3b38d56331f6c3dac3e86adbbd9012c2159 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:22:43 +0000 Subject: [PATCH 04/45] Bump actions/setup-dotnet from 3 to 4 (#38) --- .github/workflows/build.yml | 6 +++--- .github/workflows/codeql.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 881e2ed..58d1e5d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: contents: read steps: - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x @@ -149,7 +149,7 @@ jobs: contents: read steps: - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x @@ -202,7 +202,7 @@ jobs: contents: read steps: - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5b1868e..9a58eeb 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,7 +24,7 @@ jobs: language: [ 'csharp' ] steps: - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: | 6.0.x From 0b0b95b5e75e5346ebfb2220b1ef25cd92bbe02f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 09:25:01 +0000 Subject: [PATCH 05/45] Bump jetbrains.resharper.globaltools from 2023.3.0 to 2023.3.1 (#40) --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 81fbd25..e805276 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.0", + "version": "2023.3.1", "commands": [ "jb" ] @@ -21,4 +21,4 @@ ] } } -} +} \ No newline at end of file From 05f097bb4c0d6720354b2a7158b9f31cdb6ac162 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:24:21 +0100 Subject: [PATCH 06/45] Bump github/codeql-action from 2 to 3 (#43) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9a58eeb..eb03757 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,12 +32,12 @@ jobs: - name: Git checkout uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" From a15dd01d55860bb4be4ad8918aa970d0851fef45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 02:12:27 +0100 Subject: [PATCH 07/45] Bump actions/download-artifact from 3 to 4 (#41) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 58d1e5d..28a547b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -245,7 +245,7 @@ jobs: contents: write steps: - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Publish to GitHub Packages if: github.event_name == 'push' || github.event_name == 'release' env: From 69485e422defcc4409b995c392ad2f727515e298 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 02:12:58 +0100 Subject: [PATCH 08/45] Bump actions/upload-artifact from 3 to 4 (#42) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28a547b..87979b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -133,7 +133,7 @@ jobs: dotnet pack --no-build --configuration Release --output $env:GITHUB_WORKSPACE/artifacts/packages /p:VersionSuffix=$env:PACKAGE_VERSION_SUFFIX - name: Upload packages to artifacts if: matrix.os == 'ubuntu-latest' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: packages path: artifacts/packages From b0fff30fe874001845a57af206d2a8aa1f4118e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:44:16 +0000 Subject: [PATCH 09/45] Bump jetbrains.resharper.globaltools from 2023.3.1 to 2023.3.2 Bumps jetbrains.resharper.globaltools from 2023.3.1 to 2023.3.2. --- updated-dependencies: - dependency-name: jetbrains.resharper.globaltools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e805276..e1a5fb8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.1", + "version": "2023.3.2", "commands": [ "jb" ] From b6d98667879579b09e8c879946879418b9056c8a Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:27:17 +0100 Subject: [PATCH 10/45] Resharper: Fix new warning about usage of collection expressions --- .../BaseForAtomicOperationsTestsThatChangeOptions.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs index 44397df..6f60018 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs @@ -48,10 +48,7 @@ private static void CopyPropertyValues(JsonApiOptions source, JsonApiOptions des { foreach (PropertyInfo property in PropertyCache) { - property.SetMethod!.Invoke(destination, new[] - { - property.GetMethod!.Invoke(source, null) - }); + property.SetMethod!.Invoke(destination, [property.GetMethod!.Invoke(source, null)]); } } } From 9eb8929308b0765b95fa40eaf4fca6d8d857f401 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:37:45 +0000 Subject: [PATCH 11/45] Bump actions/dependency-review-action from 3 to 4 (#45) --- .github/workflows/deps-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deps-review.yml b/.github/workflows/deps-review.yml index b994508..b9d6d20 100644 --- a/.github/workflows/deps-review.yml +++ b/.github/workflows/deps-review.yml @@ -11,4 +11,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@v4 - name: 'Dependency Review' - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 From 5d16e49d44cc05c86221565a6229c66b45e92439 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:21:00 +0000 Subject: [PATCH 12/45] Bump jetbrains.resharper.globaltools from 2023.3.2 to 2023.3.3 (#46) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e1a5fb8..4dbc193 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.2", + "version": "2023.3.3", "commands": [ "jb" ] From 2397e8f6900f231a0ab7c1d859a5ffebda356463 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 23:55:03 +0000 Subject: [PATCH 13/45] Bump dotnet-reportgenerator-globaltool from 5.2.0 to 5.2.1 (#48) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4dbc193..fe0a0d8 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.0", + "version": "5.2.1", "commands": [ "reportgenerator" ] From 9027ffa6bfcbd6d1cadcc1374e0308e309189ca9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 00:23:10 +0000 Subject: [PATCH 14/45] Bump codecov/codecov-action from 3 to 4 (#47) --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87979b1..c12ff81 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,7 +123,9 @@ jobs: dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --logger "GitHubActions;summary.includeSkippedTests=true" - name: Upload coverage to codecov.io if: matrix.os == 'ubuntu-latest' - uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + uses: codecov/codecov-action@v4 with: fail_ci_if_error: true verbose: true From 220a2801a23acae226614326ebb0a0bc9ddeb181 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 02:55:34 +0000 Subject: [PATCH 15/45] Bump dotnet-reportgenerator-globaltool from 5.2.1 to 5.2.2 (#49) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index fe0a0d8..a711e98 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.1", + "version": "5.2.2", "commands": [ "reportgenerator" ] From 57ac32a5c3d26e0e53e4c57a9828f37fba7ab3a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 02:28:27 +0000 Subject: [PATCH 16/45] Bump jetbrains.resharper.globaltools from 2023.3.3 to 2023.3.4 (#50) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a711e98..8988987 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.3", + "version": "2023.3.4", "commands": [ "jb" ] From bba7ae94b812ca001e3d2c7d405e995d6557d6a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:31:08 +0000 Subject: [PATCH 17/45] Bump dotnet-reportgenerator-globaltool from 5.2.2 to 5.2.3 (#51) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8988987..c156243 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.2", + "version": "5.2.3", "commands": [ "reportgenerator" ] From 0cc27d48d5f8c6b4623694180b03ea9e073e781d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 23 Mar 2024 15:50:01 +0000 Subject: [PATCH 18/45] Bump dotnet-reportgenerator-globaltool from 5.2.3 to 5.2.4 (#52) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c156243..01c1359 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.3", + "version": "5.2.4", "commands": [ "reportgenerator" ] From 25a998fcf86f714fced50a275822529e3a8f75a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:31:46 +0000 Subject: [PATCH 19/45] Bump jetbrains.resharper.globaltools from 2023.3.4 to 2024.1.0 (#53) --- .config/dotnet-tools.json | 2 +- .github/workflows/build.yml | 10 +++++++- JsonApiDotNetCore.MongoDb.sln.DotSettings | 7 ++++++ inspectcode.ps1 | 2 +- .../Controllers/OperationsController.cs | 11 +++------ .../Definitions/TodoItemDefinition.cs | 23 ++++++++----------- ...ComparisonInFilterNotSupportedException.cs | 12 +++------- .../UnsupportedRelationshipException.cs | 12 +++------- .../Creating/AtomicCreateResourceTests.cs | 9 ++------ ...eateResourceWithToManyRelationshipTests.cs | 9 ++------ ...reateResourceWithToOneRelationshipTests.cs | 9 ++------ .../Deleting/AtomicDeleteResourceTests.cs | 9 ++------ ...mplicitlyChangingTextLanguageDefinition.cs | 12 ++++------ .../LocalIds/AtomicLocalIdTests.cs | 9 ++------ .../Meta/MusicTrackMetaDefinition.cs | 8 ++----- .../Meta/TextLanguageMetaDefinition.cs | 8 ++----- .../Mixed/MaximumOperationsPerRequestTests.cs | 10 ++------ .../AtomicOperations/OperationsController.cs | 11 +++------ .../AtomicOperations/OperationsDbContext.cs | 7 +----- .../Transactions/MusicTrackRepository.cs | 13 ++++------- .../AtomicAddToToManyRelationshipTests.cs | 9 ++------ ...AtomicRemoveFromToManyRelationshipTests.cs | 9 ++------ .../AtomicReplaceToManyRelationshipTests.cs | 9 ++------ .../AtomicUpdateToOneRelationshipTests.cs | 9 ++------ .../AtomicReplaceToManyRelationshipTests.cs | 9 ++------ .../Resources/AtomicUpdateResourceTests.cs | 9 ++------ .../AtomicUpdateToOneRelationshipTests.cs | 9 ++------ .../HitCountingResourceDefinition.cs | 11 +++------ .../IntegrationTests/Meta/MetaDbContext.cs | 7 +----- .../Meta/SupportTicketDefinition.cs | 8 ++----- .../QueryStrings/Filtering/FilterDbContext.cs | 7 +----- .../QueryStrings/QueryStringDbContext.cs | 7 +----- .../ResultCapturingRepository.cs | 16 +++++-------- .../ImplicitlyChangingWorkItemDefinition.cs | 11 +++------ ...plicitlyChangingWorkItemGroupDefinition.cs | 11 +++------ .../ReadWrite/ReadWriteDbContext.cs | 7 +----- .../Reading/MoonDefinition.cs | 10 ++------ .../Reading/PlanetDefinition.cs | 14 +++-------- .../Reading/StarDefinition.cs | 10 ++------ .../Reading/UniverseDbContext.cs | 7 +----- test/TestBuildingBlocks/MongoDbContextShim.cs | 9 ++------ .../TestBuildingBlocks/MongoRunnerProvider.cs | 12 +++------- 42 files changed, 114 insertions(+), 289 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 01c1359..caa9982 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2023.3.4", + "version": "2024.1.0", "commands": [ "jb" ] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c12ff81..e10312d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,8 @@ jobs: permissions: contents: read steps: + - name: Tune GitHub-hosted runner network + uses: smorimoto/tune-github-hosted-runner-network@v1 - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -150,6 +152,8 @@ jobs: permissions: contents: read steps: + - name: Tune GitHub-hosted runner network + uses: smorimoto/tune-github-hosted-runner-network@v1 - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -166,7 +170,7 @@ jobs: run: | $inspectCodeOutputPath = Join-Path $env:RUNNER_TEMP 'jetbrains-inspectcode-results.xml' Write-Output "INSPECT_CODE_OUTPUT_PATH=$inspectCodeOutputPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - dotnet jb inspectcode JsonApiDotNetCore.MongoDb.sln --build --dotnetcoresdk=$(dotnet --version) --output="$inspectCodeOutputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --properties:ContinuousIntegrationBuild=false --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal + dotnet jb inspectcode JsonApiDotNetCore.MongoDb.sln --build --dotnetcoresdk=$(dotnet --version) --output="$inspectCodeOutputPath" --format="xml" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --properties:ContinuousIntegrationBuild=false --properties:RunAnalyzers=false --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal - name: Verify outcome shell: pwsh run: | @@ -203,6 +207,8 @@ jobs: permissions: contents: read steps: + - name: Tune GitHub-hosted runner network + uses: smorimoto/tune-github-hosted-runner-network@v1 - name: Setup .NET uses: actions/setup-dotnet@v4 with: @@ -246,6 +252,8 @@ jobs: packages: write contents: write steps: + - name: Tune GitHub-hosted runner network + uses: smorimoto/tune-github-hosted-runner-network@v1 - name: Download artifacts uses: actions/download-artifact@v4 - name: Publish to GitHub Packages diff --git a/JsonApiDotNetCore.MongoDb.sln.DotSettings b/JsonApiDotNetCore.MongoDb.sln.DotSettings index d7699ea..2cd13da 100644 --- a/JsonApiDotNetCore.MongoDb.sln.DotSettings +++ b/JsonApiDotNetCore.MongoDb.sln.DotSettings @@ -54,10 +54,12 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); WARNING WARNING WARNING + DO_NOT_SHOW WARNING SUGGESTION HINT WARNING + SUGGESTION DO_NOT_SHOW HINT SUGGESTION @@ -95,6 +97,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); SUGGESTION SUGGESTION WARNING + True SUGGESTION <?xml version="1.0" encoding="utf-16"?><Profile name="JADNC Full Cleanup"><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" ArrangeNullCheckingPattern="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSReformatInactiveBranches>True</CSReformatInactiveBranches></Profile> JADNC Full Cleanup @@ -154,6 +157,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); WRAP_IF_LONG CHOP_ALWAYS CHOP_ALWAYS + WRAP_IF_LONG True True 2 @@ -586,11 +590,14 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); False <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy> True True True True True + True True Replace argument null check using throw expression with Guard clause True diff --git a/inspectcode.ps1 b/inspectcode.ps1 index 13f0abb..25195fe 100644 --- a/inspectcode.ps1 +++ b/inspectcode.ps1 @@ -10,7 +10,7 @@ if ($LastExitCode -ne 0) { $outputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'jetbrains-inspectcode-results.xml') $resultPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), 'jetbrains-inspectcode-results.html') -dotnet jb inspectcode JsonApiDotNetCore.MongoDb.sln --dotnetcoresdk=$(dotnet --version) --build --output="$outputPath" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal +dotnet jb inspectcode JsonApiDotNetCore.MongoDb.sln --dotnetcoresdk=$(dotnet --version) --build --output="$outputPath" --format="xml" --profile=WarningSeverities.DotSettings --properties:Configuration=Release --properties:RunAnalyzers=false --severity=WARNING --verbosity=WARN -dsl=GlobalAll -dsl=GlobalPerProduct -dsl=SolutionPersonal -dsl=ProjectPersonal if ($LastExitCode -ne 0) { throw "Code inspection failed with exit code $LastExitCode" diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs index 8172459..82f602d 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs @@ -6,11 +6,6 @@ namespace JsonApiDotNetCoreMongoDbExample.Controllers; -public sealed class OperationsController : JsonApiOperationsController -{ - public OperationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, - IJsonApiRequest request, ITargetedFields targetedFields) - : base(options, resourceGraph, loggerFactory, processor, request, targetedFields) - { - } -} +public sealed class OperationsController( + IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields); diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs index 970a061..7910a8e 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs @@ -12,22 +12,19 @@ namespace JsonApiDotNetCoreMongoDbExample.Definitions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TodoItemDefinition : JsonApiResourceDefinition +public sealed class TodoItemDefinition( + IResourceGraph resourceGraph, +#if NET6_0 + ISystemClock systemClock +#else + TimeProvider timeProvider +#endif +) : JsonApiResourceDefinition(resourceGraph) { - private readonly Func _getUtcNow; - #if NET6_0 - public TodoItemDefinition(IResourceGraph resourceGraph, ISystemClock systemClock) - : base(resourceGraph) - { - _getUtcNow = () => systemClock.UtcNow; - } + private readonly Func _getUtcNow = () => systemClock.UtcNow; #else - public TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider) - : base(resourceGraph) - { - _getUtcNow = timeProvider.GetUtcNow; - } + private readonly Func _getUtcNow = timeProvider.GetUtcNow; #endif public override SortExpression OnApplySort(SortExpression? existingSort) diff --git a/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs b/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs index 4b7508b..55c865a 100644 --- a/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs +++ b/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs @@ -10,13 +10,7 @@ namespace JsonApiDotNetCore.MongoDb.Errors; /// https://jira.mongodb.org/browse/CSHARP-1592. /// [PublicAPI] -public sealed class AttributeComparisonInFilterNotSupportedException : JsonApiException +public sealed class AttributeComparisonInFilterNotSupportedException() : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) { - public AttributeComparisonInFilterNotSupportedException() - : base(new ErrorObject(HttpStatusCode.BadRequest) - { - Title = "Comparing attributes against each other is not supported when using MongoDB." - }) - { - } -} + Title = "Comparing attributes against each other is not supported when using MongoDB." +}); diff --git a/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs b/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs index 2526e59..01852e5 100644 --- a/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs +++ b/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs @@ -9,13 +9,7 @@ namespace JsonApiDotNetCore.MongoDb.Errors; /// The error that is thrown when the user attempts to fetch, create or update a relationship. /// [PublicAPI] -public sealed class UnsupportedRelationshipException : JsonApiException +public sealed class UnsupportedRelationshipException() : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) { - public UnsupportedRelationshipException() - : base(new ErrorObject(HttpStatusCode.BadRequest) - { - Title = "Relationships are not supported when using MongoDB." - }) - { - } -} + Title = "Relationships are not supported when using MongoDB." +}); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs index 4a7e660..0172c7a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs @@ -8,16 +8,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Creating; [Collection("AtomicOperationsFixture")] -public sealed class AtomicCreateResourceTests +public sealed class AtomicCreateResourceTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicCreateResourceTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Can_create_resource() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs index ad022d4..7fa0e1c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Creating; [Collection("AtomicOperationsFixture")] -public sealed class AtomicCreateResourceWithToManyRelationshipTests +public sealed class AtomicCreateResourceWithToManyRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicCreateResourceWithToManyRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_create_ToMany_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs index 209cdea..f33a634 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Creating; [Collection("AtomicOperationsFixture")] -public sealed class AtomicCreateResourceWithToOneRelationshipTests +public sealed class AtomicCreateResourceWithToOneRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicCreateResourceWithToOneRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_create_ToOne_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs index 14f62fb..9e401bd 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Deleting; [Collection("AtomicOperationsFixture")] -public sealed class AtomicDeleteResourceTests +public sealed class AtomicDeleteResourceTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicDeleteResourceTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Can_delete_existing_resource() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/ImplicitlyChangingTextLanguageDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/ImplicitlyChangingTextLanguageDefinition.cs index 946bc14..822cfd4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/ImplicitlyChangingTextLanguageDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/ImplicitlyChangingTextLanguageDefinition.cs @@ -10,17 +10,13 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; /// Used to simulate side effects that occur in the database while saving, typically caused by database triggers. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public abstract class ImplicitlyChangingTextLanguageDefinition : HitCountingResourceDefinition +public abstract class ImplicitlyChangingTextLanguageDefinition( + IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter, IMongoDataAccess mongoDataAccess) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { internal const string Suffix = " (changed)"; - private readonly IMongoDataAccess _mongoDataAccess; - - protected ImplicitlyChangingTextLanguageDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter, IMongoDataAccess mongoDataAccess) - : base(resourceGraph, hitCounter) - { - _mongoDataAccess = mongoDataAccess; - } + private readonly IMongoDataAccess _mongoDataAccess = mongoDataAccess; public override async Task OnWriteSucceededAsync(TextLanguage resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs index b9a2080..54680ba 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.LocalIds; [Collection("AtomicOperationsFixture")] -public sealed class AtomicLocalIdTests +public sealed class AtomicLocalIdTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicLocalIdTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Can_update_resource_using_local_ID() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/MusicTrackMetaDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/MusicTrackMetaDefinition.cs index 479898d..93cf7d2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/MusicTrackMetaDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/MusicTrackMetaDefinition.cs @@ -4,15 +4,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Meta; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class MusicTrackMetaDefinition : HitCountingResourceDefinition +public sealed class MusicTrackMetaDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.GetMeta; - public MusicTrackMetaDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph, hitCounter) - { - } - public override IDictionary GetMeta(MusicTrack resource) { base.GetMeta(resource); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/TextLanguageMetaDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/TextLanguageMetaDefinition.cs index bf1bd66..2d440be 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/TextLanguageMetaDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/TextLanguageMetaDefinition.cs @@ -5,17 +5,13 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Meta; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TextLanguageMetaDefinition : ImplicitlyChangingTextLanguageDefinition +public sealed class TextLanguageMetaDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter, IMongoDataAccess mongoDataAccess) + : ImplicitlyChangingTextLanguageDefinition(resourceGraph, hitCounter, mongoDataAccess) { internal const string NoticeText = "See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes for ISO 639-1 language codes."; protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.GetMeta; - public TextLanguageMetaDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter, IMongoDataAccess mongoDataAccess) - : base(resourceGraph, hitCounter, mongoDataAccess) - { - } - public override IDictionary GetMeta(TextLanguage resource) { base.GetMeta(resource); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs index b3f8d35..ba4bf6a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs @@ -8,15 +8,9 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Mixed; [Collection("AtomicOperationsFixture")] -public sealed class MaximumOperationsPerRequestTests : BaseForAtomicOperationsTestsThatChangeOptions +public sealed class MaximumOperationsPerRequestTests(AtomicOperationsFixture fixture) : BaseForAtomicOperationsTestsThatChangeOptions(fixture) { - private readonly IntegrationTestContext _testContext; - - public MaximumOperationsPerRequestTests(AtomicOperationsFixture fixture) - : base(fixture) - { - _testContext = fixture.TestContext; - } + private readonly IntegrationTestContext _testContext = fixture.TestContext; [Fact] public async Task Can_process_high_number_of_operations_when_unconstrained() diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs index f82e32d..c7ce7f9 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs @@ -7,11 +7,6 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; -public sealed class OperationsController : JsonApiOperationsController -{ - public OperationsController(IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, - IJsonApiRequest request, ITargetedFields targetedFields) - : base(options, resourceGraph, loggerFactory, processor, request, targetedFields) - { - } -} +public sealed class OperationsController( + IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs index 6b6f6f0..aa75333 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class OperationsDbContext : MongoDbContextShim +public sealed class OperationsDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim Playlists => Set(); public MongoDbSetShim MusicTracks => Set(); @@ -13,9 +13,4 @@ public sealed class OperationsDbContext : MongoDbContextShim public MongoDbSetShim TextLanguages => Set(); public MongoDbSetShim Performers => Set(); public MongoDbSetShim RecordCompanies => Set(); - - public OperationsDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs index 9595112..bd73d6c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/MusicTrackRepository.cs @@ -8,14 +8,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Transactions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class MusicTrackRepository : MongoRepository +public sealed class MusicTrackRepository( + IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, + IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder) + : MongoRepository(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, + queryableBuilder) { public override string? TransactionId => null; - - public MusicTrackRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, - IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, - IQueryableBuilder queryableBuilder) - : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs index d15f9d0..b81b3f4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Relationships; [Collection("AtomicOperationsFixture")] -public sealed class AtomicAddToToManyRelationshipTests +public sealed class AtomicAddToToManyRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicAddToToManyRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_add_to_OneToMany_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs index 857f7c5..71bfc3d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Relationships; [Collection("AtomicOperationsFixture")] -public sealed class AtomicRemoveFromToManyRelationshipTests +public sealed class AtomicRemoveFromToManyRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicRemoveFromToManyRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_remove_from_OneToMany_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs index b716339..04c3bc3 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Relationships; [Collection("AtomicOperationsFixture")] -public sealed class AtomicReplaceToManyRelationshipTests +public sealed class AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_replace_OneToMany_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs index dccab27..bcbdcca 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Relationships; [Collection("AtomicOperationsFixture")] -public sealed class AtomicUpdateToOneRelationshipTests +public sealed class AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_create_ManyToOne_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs index 833bdd3..c65167b 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Resources; [Collection("AtomicOperationsFixture")] -public sealed class AtomicReplaceToManyRelationshipTests +public sealed class AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_replace_ToMany_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs index 7ae1426..a123409 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Resources; [Collection("AtomicOperationsFixture")] -public sealed class AtomicUpdateResourceTests +public sealed class AtomicUpdateResourceTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicUpdateResourceTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Can_update_resources() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs index df60664..014b396 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs @@ -7,16 +7,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Updating.Resources; [Collection("AtomicOperationsFixture")] -public sealed class AtomicUpdateToOneRelationshipTests +public sealed class AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture fixture) { - private readonly IntegrationTestContext _testContext; + private readonly IntegrationTestContext _testContext = fixture.TestContext; private readonly OperationsFakers _fakers = new(); - public AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture fixture) - { - _testContext = fixture.TestContext; - } - [Fact] public async Task Cannot_create_ToOne_relationship() { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs index fd93e5e..3ee880d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs @@ -11,19 +11,14 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests; /// Tracks invocations on callback methods. This is used solely in our tests, so we can assert which /// calls were made, and in which order. /// -public abstract class HitCountingResourceDefinition : JsonApiResourceDefinition +public abstract class HitCountingResourceDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : JsonApiResourceDefinition(resourceGraph) where TResource : class, IIdentifiable { - private readonly ResourceDefinitionHitCounter _hitCounter; + private readonly ResourceDefinitionHitCounter _hitCounter = hitCounter; protected virtual ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.All; - protected HitCountingResourceDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph) - { - _hitCounter = hitCounter; - } - public override IImmutableSet OnApplyIncludes(IImmutableSet existingIncludes) { if (ExtensibilityPointsToTrack.HasFlag(ResourceDefinitionExtensibilityPoints.OnApplyIncludes)) diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs index fbd2ff5..8aa82f8 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs @@ -5,12 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class MetaDbContext : MongoDbContextShim +public sealed class MetaDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim SupportTickets => Set(); - - public MetaDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicketDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicketDefinition.cs index 2cbff91..704c6c8 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicketDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/SupportTicketDefinition.cs @@ -4,15 +4,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class SupportTicketDefinition : HitCountingResourceDefinition +public sealed class SupportTicketDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.GetMeta; - public SupportTicketDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph, hitCounter) - { - } - public override IDictionary? GetMeta(SupportTicket resource) { base.GetMeta(resource); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 356e537..58277b3 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -5,12 +5,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.Filtering; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class FilterDbContext : MongoDbContextShim +public sealed class FilterDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim FilterableResources => Set(); - - public FilterDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs index 1a3489e..a1e48b6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -5,14 +5,9 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class QueryStringDbContext : MongoDbContextShim +public sealed class QueryStringDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim Blogs => Set(); public MongoDbSetShim Posts => Set(); public MongoDbSetShim Accounts => Set(); - - public QueryStringDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs index b88cd2e..f00897c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs @@ -11,18 +11,14 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.SparseFiel /// Enables sparse fieldset tests to verify which fields were (not) retrieved from the database. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class ResultCapturingRepository : MongoRepository +public sealed class ResultCapturingRepository( + IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, + IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder, + ResourceCaptureStore captureStore) : MongoRepository(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, + resourceDefinitionAccessor, queryableBuilder) where TResource : class, IIdentifiable { - private readonly ResourceCaptureStore _captureStore; - - public ResultCapturingRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, - IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, - IQueryableBuilder queryableBuilder, ResourceCaptureStore captureStore) - : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) - { - _captureStore = captureStore; - } + private readonly ResourceCaptureStore _captureStore = captureStore; public override async Task> GetAsync(QueryLayer queryLayer, CancellationToken cancellationToken) { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs index 61f68e1..38b54cf 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs @@ -10,17 +10,12 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; /// Used to simulate side effects that occur in the database while saving, typically caused by database triggers. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class ImplicitlyChangingWorkItemDefinition : JsonApiResourceDefinition +public sealed class ImplicitlyChangingWorkItemDefinition(IResourceGraph resourceGraph, ReadWriteDbContext dbContext) + : JsonApiResourceDefinition(resourceGraph) { internal const string Suffix = " (changed)"; - private readonly ReadWriteDbContext _dbContext; - - public ImplicitlyChangingWorkItemDefinition(IResourceGraph resourceGraph, ReadWriteDbContext dbContext) - : base(resourceGraph) - { - _dbContext = dbContext; - } + private readonly ReadWriteDbContext _dbContext = dbContext; public override Task OnWriteSucceededAsync(WorkItem resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs index 3d4e846..ef8c375 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs @@ -10,17 +10,12 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; /// Used to simulate side effects that occur in the database while saving, typically caused by database triggers. /// [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class ImplicitlyChangingWorkItemGroupDefinition : JsonApiResourceDefinition +public sealed class ImplicitlyChangingWorkItemGroupDefinition(IResourceGraph resourceGraph, ReadWriteDbContext dbContext) + : JsonApiResourceDefinition(resourceGraph) { internal const string Suffix = " (changed)"; - private readonly ReadWriteDbContext _dbContext; - - public ImplicitlyChangingWorkItemGroupDefinition(IResourceGraph resourceGraph, ReadWriteDbContext dbContext) - : base(resourceGraph) - { - _dbContext = dbContext; - } + private readonly ReadWriteDbContext _dbContext = dbContext; public override Task OnWriteSucceededAsync(WorkItemGroup resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs index f3cfd72..32a0dd9 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs @@ -5,16 +5,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ReadWriteDbContext : MongoDbContextShim +public sealed class ReadWriteDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim WorkItems => Set(); public MongoDbSetShim WorkTags => Set(); public MongoDbSetShim Groups => Set(); public MongoDbSetShim RgbColors => Set(); public MongoDbSetShim UserAccounts => Set(); - - public ReadWriteDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index 55340ea..e4e8f7a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -6,17 +6,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class MoonDefinition : HitCountingResourceDefinition +public sealed class MoonDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.Reading; - public MoonDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph, hitCounter) - { - // This constructor will be resolved from the container, which means - // you can take on any dependency that is also defined in the container. - } - public override QueryStringParameterHandlers OnRegisterQueryableHandlersForQueryStringParameters() { base.OnRegisterQueryableHandlersForQueryStringParameters(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/PlanetDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/PlanetDefinition.cs index 567fa04..c2122d6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/PlanetDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/PlanetDefinition.cs @@ -6,21 +6,13 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class PlanetDefinition : HitCountingResourceDefinition +public sealed class PlanetDefinition(IResourceGraph resourceGraph, IClientSettingsProvider clientSettingsProvider, ResourceDefinitionHitCounter hitCounter) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { - private readonly IClientSettingsProvider _clientSettingsProvider; + private readonly IClientSettingsProvider _clientSettingsProvider = clientSettingsProvider; protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.Reading; - public PlanetDefinition(IResourceGraph resourceGraph, IClientSettingsProvider clientSettingsProvider, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph, hitCounter) - { - // This constructor will be resolved from the container, which means - // you can take on any dependency that is also defined in the container. - - _clientSettingsProvider = clientSettingsProvider; - } - public override FilterExpression? OnApplyFilter(FilterExpression? existingFilter) { base.OnApplyFilter(existingFilter); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/StarDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/StarDefinition.cs index 46632bd..5a55410 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/StarDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/StarDefinition.cs @@ -6,17 +6,11 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class StarDefinition : HitCountingResourceDefinition +public sealed class StarDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : HitCountingResourceDefinition(resourceGraph, hitCounter) { protected override ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.Reading; - public StarDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : base(resourceGraph, hitCounter) - { - // This constructor will be resolved from the container, which means - // you can take on any dependency that is also defined in the container. - } - public override SortExpression OnApplySort(SortExpression? existingSort) { base.OnApplySort(existingSort); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs index f8bd720..901ce69 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs @@ -5,14 +5,9 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class UniverseDbContext : MongoDbContextShim +public sealed class UniverseDbContext(IMongoDatabase database) : MongoDbContextShim(database) { public MongoDbSetShim Stars => Set(); public MongoDbSetShim Planets => Set(); public MongoDbSetShim Moons => Set(); - - public UniverseDbContext(IMongoDatabase database) - : base(database) - { - } } diff --git a/test/TestBuildingBlocks/MongoDbContextShim.cs b/test/TestBuildingBlocks/MongoDbContextShim.cs index 0521914..abc8942 100644 --- a/test/TestBuildingBlocks/MongoDbContextShim.cs +++ b/test/TestBuildingBlocks/MongoDbContextShim.cs @@ -7,16 +7,11 @@ namespace TestBuildingBlocks; /// Provides an Entity Framework Core DbContext-like abstraction that translates to MongoDB calls. This makes it easier to keep tests in sync with the /// main repository. /// -public abstract class MongoDbContextShim +public abstract class MongoDbContextShim(IMongoDatabase database) { - private readonly IMongoDatabase _database; + private readonly IMongoDatabase _database = database; private readonly List _dbSetShims = []; - protected MongoDbContextShim(IMongoDatabase database) - { - _database = database; - } - protected MongoDbSetShim Set() where TEntity : IMongoIdentifiable { diff --git a/test/TestBuildingBlocks/MongoRunnerProvider.cs b/test/TestBuildingBlocks/MongoRunnerProvider.cs index c22b8d5..f2dc54a 100644 --- a/test/TestBuildingBlocks/MongoRunnerProvider.cs +++ b/test/TestBuildingBlocks/MongoRunnerProvider.cs @@ -54,19 +54,13 @@ private void Detach() } } - private sealed class MongoRunnerWrapper : IMongoRunner + private sealed class MongoRunnerWrapper(MongoRunnerProvider owner, IMongoRunner underlyingMongoRunner) : IMongoRunner { - private readonly MongoRunnerProvider _owner; - private IMongoRunner? _underlyingMongoRunner; + private readonly MongoRunnerProvider _owner = owner; + private IMongoRunner? _underlyingMongoRunner = underlyingMongoRunner; public string ConnectionString => _underlyingMongoRunner?.ConnectionString ?? throw new ObjectDisposedException(nameof(IMongoRunner)); - public MongoRunnerWrapper(MongoRunnerProvider owner, IMongoRunner underlyingMongoRunner) - { - _owner = owner; - _underlyingMongoRunner = underlyingMongoRunner; - } - public void Import(string database, string collection, string inputFilePath, string? additionalArguments = null, bool drop = false) { if (_underlyingMongoRunner == null) From b1d05a8d2efaa9432dd96ac468780276544e0dcc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 07:32:27 +0000 Subject: [PATCH 20/45] Bump jetbrains.resharper.globaltools from 2024.1.0 to 2024.1.2 (#56) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index caa9982..a53702e 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2024.1.0", + "version": "2024.1.2", "commands": [ "jb" ] From 25ae4c0203f9bd3fafa04fbac1a861b5b7bcb375 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 08:33:58 +0000 Subject: [PATCH 21/45] Bump dotnet-reportgenerator-globaltool from 5.2.4 to 5.2.5 (#55) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a53702e..9261671 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.4", + "version": "5.2.5", "commands": [ "reportgenerator" ] From 899a5677f77450ae373624965bea28ed693feaa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 04:25:30 +0000 Subject: [PATCH 22/45] Bump dotnet-reportgenerator-globaltool from 5.2.5 to 5.3.0 (#57) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9261671..2dc94b1 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.2.5", + "version": "5.3.0", "commands": [ "reportgenerator" ] From bd220779ddb9765651da0e35bcd433d6e8008850 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 May 2024 05:08:14 +0000 Subject: [PATCH 23/45] Bump dotnet-reportgenerator-globaltool from 5.3.0 to 5.3.4 (#58) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2dc94b1..d630912 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.0", + "version": "5.3.4", "commands": [ "reportgenerator" ] From ed4f58a18df56cfc6fb11e4ec210c761645d2fc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:22:03 +0000 Subject: [PATCH 24/45] Bump jetbrains.resharper.globaltools from 2024.1.2 to 2024.1.3 (#60) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d630912..2596063 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2024.1.2", + "version": "2024.1.3", "commands": [ "jb" ] From 1dc0da37ed102ba57dbdf4863d26f0571859cc8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jun 2024 21:47:19 +0000 Subject: [PATCH 25/45] Bump dotnet-reportgenerator-globaltool from 5.3.4 to 5.3.6 (#59) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2596063..f2653f6 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.4", + "version": "5.3.6", "commands": [ "reportgenerator" ] From 961ecefe62ae760f20befa1e0bfc74a172d77c37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 03:54:30 +0000 Subject: [PATCH 26/45] Bump jetbrains.resharper.globaltools from 2024.1.3 to 2024.1.4 (#61) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index f2653f6..869b61a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2024.1.3", + "version": "2024.1.4", "commands": [ "jb" ] From 3f6f926019dd948dae82e16b1cba72d525a68bd9 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 23:02:09 +0200 Subject: [PATCH 27/45] Revert "Resharper: Replace async method with Task return" This reverts commit b95817446d6b59d5fd92269a56420a1a37489d04. --- .../AtomicOperations/MongoTransaction.cs | 12 ++++------ .../Repositories/MongoRepository.cs | 4 ++-- .../AtomicOperationsFixture.cs | 4 ++-- ...reateResourceWithClientGeneratedIdTests.cs | 4 ++-- ...eateResourceWithToManyRelationshipTests.cs | 4 ++-- ...reateResourceWithToOneRelationshipTests.cs | 4 ++-- .../Deleting/AtomicDeleteResourceTests.cs | 4 ++-- .../Meta/AtomicResourceMetaTests.cs | 4 ++-- .../Transactions/AtomicRollbackTests.cs | 6 ++--- .../Transactions/LyricRepository.cs | 4 ++-- .../AtomicAddToToManyRelationshipTests.cs | 8 +++---- ...AtomicRemoveFromToManyRelationshipTests.cs | 8 +++---- .../AtomicReplaceToManyRelationshipTests.cs | 8 +++---- .../AtomicUpdateToOneRelationshipTests.cs | 4 ++-- .../AtomicReplaceToManyRelationshipTests.cs | 4 ++-- .../Resources/AtomicUpdateResourceTests.cs | 16 ++++++------- .../AtomicUpdateToOneRelationshipTests.cs | 4 ++-- .../Meta/TopLevelCountTests.cs | 6 ++--- .../SparseFieldSets/SparseFieldSetTests.cs | 20 ++++++++-------- ...reateResourceWithClientGeneratedIdTests.cs | 4 ++-- ...eateResourceWithToManyRelationshipTests.cs | 4 ++-- ...reateResourceWithToOneRelationshipTests.cs | 4 ++-- .../ReadWrite/Deleting/DeleteResourceTests.cs | 4 ++-- .../Fetching/FetchRelationshipTests.cs | 12 +++++----- .../ReadWrite/Fetching/FetchResourceTests.cs | 16 ++++++------- .../ImplicitlyChangingWorkItemDefinition.cs | 6 ++--- ...plicitlyChangingWorkItemGroupDefinition.cs | 6 ++--- .../AddToToManyRelationshipTests.cs | 8 +++---- .../RemoveFromToManyRelationshipTests.cs | 8 +++---- .../ReplaceToManyRelationshipTests.cs | 8 +++---- .../UpdateToOneRelationshipTests.cs | 4 ++-- .../ReplaceToManyRelationshipTests.cs | 8 +++---- .../Updating/Resources/UpdateResourceTests.cs | 24 +++++++++---------- .../Resources/UpdateToOneRelationshipTests.cs | 4 ++-- .../Reading/ResourceDefinitionReadTests.cs | 20 ++++++++-------- test/TestBuildingBlocks/IntegrationTest.cs | 24 +++++++++---------- test/TestBuildingBlocks/MongoDbContextShim.cs | 4 ++-- test/TestBuildingBlocks/MongoDbSetShim.cs | 12 +++++----- 38 files changed, 150 insertions(+), 158 deletions(-) diff --git a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs index 3ea882d..3514e87 100644 --- a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs +++ b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs @@ -35,24 +35,20 @@ public Task AfterProcessOperationAsync(CancellationToken cancellationToken) } /// - public Task CommitAsync(CancellationToken cancellationToken) + public async Task CommitAsync(CancellationToken cancellationToken) { if (_ownsTransaction && _mongoDataAccess.ActiveSession != null) { - return _mongoDataAccess.ActiveSession.CommitTransactionAsync(cancellationToken); + await _mongoDataAccess.ActiveSession.CommitTransactionAsync(cancellationToken); } - - return Task.CompletedTask; } /// - public ValueTask DisposeAsync() + public async ValueTask DisposeAsync() { if (_ownsTransaction) { - return _mongoDataAccess.DisposeAsync(); + await _mongoDataAccess.DisposeAsync(); } - - return ValueTask.CompletedTask; } } diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index c955f8c..ffdf9e1 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -174,9 +174,9 @@ public virtual async Task CreateAsync(TResource resourceFromRequest, TResource r await _resourceDefinitionAccessor.OnWritingAsync(resourceForDatabase, WriteOperationKind.CreateResource, cancellationToken); await SaveChangesAsync( - () => _mongoDataAccess.ActiveSession != null + async () => await (_mongoDataAccess.ActiveSession != null ? Collection.InsertOneAsync(_mongoDataAccess.ActiveSession, resourceForDatabase, cancellationToken: cancellationToken) - : Collection.InsertOneAsync(resourceForDatabase, cancellationToken: cancellationToken), cancellationToken); + : Collection.InsertOneAsync(resourceForDatabase, cancellationToken: cancellationToken)), cancellationToken); await _resourceDefinitionAccessor.OnWriteSucceededAsync(resourceForDatabase, WriteOperationKind.CreateResource, cancellationToken); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs index 21db655..262a2b6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsFixture.cs @@ -32,8 +32,8 @@ public Task InitializeAsync() return Task.CompletedTask; } - public Task DisposeAsync() + public async Task DisposeAsync() { - return TestContext.DisposeAsync(); + await TestContext.DisposeAsync(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs index 27813bc..7fdf996 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs @@ -132,10 +132,10 @@ public async Task Cannot_create_resource_for_existing_client_generated_ID() string newIsoCode = _fakers.TextLanguage.Generate().IsoCode!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.TextLanguages.Add(existingLanguage); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs index 7fa0e1c..70b5468 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs @@ -20,10 +20,10 @@ public async Task Cannot_create_ToMany_relationship() string newTitle = _fakers.MusicTrack.Generate().Title; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs index f33a634..51fd385 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs @@ -20,10 +20,10 @@ public async Task Cannot_create_ToOne_relationship() string newLyricText = _fakers.Lyric.Generate().Text; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs index 9e401bd..cd7accf 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs @@ -18,10 +18,10 @@ public async Task Can_delete_existing_resource() // Arrange Performer existingPerformer = _fakers.Performer.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs index ed87024..a701256 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs @@ -112,10 +112,10 @@ public async Task Returns_resource_meta_in_update_resource_with_side_effects() TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.TextLanguages.Add(existingLanguage); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs index 26ea797..0a2cc3a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs @@ -26,7 +26,7 @@ public async Task Can_rollback_created_resource_on_error() string newArtistName = _fakers.Performer.Generate().ArtistName!; DateTimeOffset newBornAt = _fakers.Performer.Generate().BornAt; - await _testContext.RunOnDatabaseAsync(dbContext => dbContext.ClearTableAsync()); + await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.ClearTableAsync()); string unknownPerformerId = Unknown.StringId.For(); @@ -91,10 +91,10 @@ public async Task Can_rollback_updated_resource_on_error() string newArtistName = _fakers.Performer.Generate().ArtistName!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string unknownPerformerId = Unknown.StringId.For(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs index d738e8d..cda8d04 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs @@ -26,8 +26,8 @@ public LyricRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete _transaction = factory.BeginTransactionAsync(CancellationToken.None).Result; } - public ValueTask DisposeAsync() + public async ValueTask DisposeAsync() { - return _transaction.DisposeAsync(); + await _transaction.DisposeAsync(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs index b81b3f4..50382cb 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_add_to_OneToMany_relationship() MusicTrack existingTrack = _fakers.MusicTrack.Generate(); Performer existingPerformer = _fakers.Performer.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -76,11 +76,11 @@ public async Task Cannot_add_to_ManyToMany_relationship() Playlist existingPlaylist = _fakers.Playlist.Generate(); MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); dbContext.Playlists.Add(existingPlaylist); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs index 71bfc3d..8ecb491 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_remove_from_OneToMany_relationship() MusicTrack existingTrack = _fakers.MusicTrack.Generate(); existingTrack.Performers = _fakers.Performer.Generate(1); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingTrack.Performers[0]); dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -76,11 +76,11 @@ public async Task Cannot_remove_from_ManyToMany_relationship() Playlist existingPlaylist = _fakers.Playlist.Generate(); existingPlaylist.Tracks = _fakers.MusicTrack.Generate(1); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingPlaylist.Tracks[0]); dbContext.Playlists.Add(existingPlaylist); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs index 04c3bc3..09f2979 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_replace_OneToMany_relationship() MusicTrack existingTrack = _fakers.MusicTrack.Generate(); Performer existingPerformer = _fakers.Performer.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -76,11 +76,11 @@ public async Task Cannot_replace_ManyToMany_relationship() Playlist existingPlaylist = _fakers.Playlist.Generate(); MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); dbContext.Playlists.Add(existingPlaylist); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs index bcbdcca..2541703 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_create_ManyToOne_relationship() MusicTrack existingTrack = _fakers.MusicTrack.Generate(); RecordCompany existingCompany = _fakers.RecordCompany.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RecordCompanies.Add(existingCompany); dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs index c65167b..329d74d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_replace_ToMany_relationship() MusicTrack existingTrack = _fakers.MusicTrack.Generate(); Performer existingPerformer = _fakers.Performer.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Performers.Add(existingPerformer); dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs index a123409..9b88500 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs @@ -84,10 +84,10 @@ public async Task Can_update_resource_without_attributes_or_relationships() // Arrange MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -139,10 +139,10 @@ public async Task Can_partially_update_resource_without_side_effects() string newGenre = _fakers.MusicTrack.Generate().Genre!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -197,10 +197,10 @@ public async Task Can_completely_update_resource_without_side_effects() string newGenre = _fakers.MusicTrack.Generate().Genre!; DateTimeOffset newReleasedAt = _fakers.MusicTrack.Generate().ReleasedAt; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -254,10 +254,10 @@ public async Task Can_update_resource_with_side_effects() TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); string newIsoCode = _fakers.TextLanguage.Generate().IsoCode!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.TextLanguages.Add(existingLanguage); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs index 014b396..75734f4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs @@ -19,11 +19,11 @@ public async Task Cannot_create_ToOne_relationship() Lyric existingLyric = _fakers.Lyric.Generate(); MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.MusicTracks.Add(existingTrack); dbContext.Lyrics.Add(existingLyric); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs index cd122ae..13bb952 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -59,7 +59,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Renders_resource_count_for_empty_collection() { // Arrange - await _testContext.RunOnDatabaseAsync(dbContext => dbContext.ClearTableAsync()); + await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.ClearTableAsync()); const string route = "/supportTickets"; @@ -111,10 +111,10 @@ public async Task Hides_resource_count_in_update_resource_response() string newDescription = _fakers.SupportTicket.Generate().Description; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.SupportTickets.Add(existingTicket); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index b61ebac..8b94f13 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -120,10 +120,10 @@ public async Task Can_select_attribute_in_primary_resource_by_ID() BlogPost post = _fakers.BlogPost.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Posts.Add(post); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/blogPosts/{post.StringId}?fields[blogPosts]=url"; @@ -151,10 +151,10 @@ public async Task Cannot_select_fields_of_ManyToOne_relationship() // Arrange BlogPost post = _fakers.BlogPost.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Posts.Add(post); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/blogPosts/{post.StringId}?fields[webAccounts]=displayName,emailAddress,preferences"; @@ -180,10 +180,10 @@ public async Task Cannot_select_fields_of_OneToMany_relationship() // Arrange WebAccount account = _fakers.WebAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Accounts.Add(account); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/webAccounts/{account.StringId}?fields[blogPosts]=caption,labels"; @@ -209,10 +209,10 @@ public async Task Cannot_select_fields_of_ManyToMany_relationship() // Arrange BlogPost post = _fakers.BlogPost.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Posts.Add(post); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/blogPosts/{post.StringId}?fields[labels]=color"; @@ -312,10 +312,10 @@ public async Task Fetches_all_scalar_properties_when_fieldset_contains_readonly_ Blog blog = _fakers.Blog.Generate(); blog.IsPublished = true; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Blogs.Add(blog); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/blogs/{blog.StringId}?fields[blogs]=showAdvertisements"; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index c763529..c9c732a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -202,10 +202,10 @@ public async Task Cannot_create_resource_for_existing_client_generated_ID() string newDisplayName = _fakers.RgbColor.Generate().DisplayName; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingColor); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index f021d9b..e0d92de 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -26,10 +26,10 @@ public async Task Cannot_create_resource_with_ToMany_relationship() // Arrange UserAccount? existingUserAccount = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingUserAccount); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index f02131d..be6732c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -29,11 +29,11 @@ public async Task Cannot_create_resource_with_ToOne_relationship() string newGroupName = _fakers.WorkItemGroup.Generate().Name; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingGroup.Color); dbContext.Groups.Add(existingGroup); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs index c00fd07..3e1bb87 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs @@ -26,10 +26,10 @@ public async Task Can_delete_existing_resource() // Arrange WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{existingWorkItem.StringId}"; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs index a7f74eb..6a9e32c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs @@ -26,10 +26,10 @@ public async Task Cannot_get_ManyToOne_relationship() { WorkItem workItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{workItem.StringId}/relationships/assignee"; @@ -55,10 +55,10 @@ public async Task Cannot_get_OneToMany_relationship() // Arrange UserAccount userAccount = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(userAccount); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/userAccounts/{userAccount.StringId}/relationships/assignedItems"; @@ -84,10 +84,10 @@ public async Task Cannot_get_ManyToMany_relationship() // Arrange WorkItem workItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{workItem.StringId}/relationships/tags"; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index c8ff584..39a52d6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -65,10 +65,10 @@ public async Task Can_get_primary_resource_by_ID() // Arrange WorkItem workItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{workItem.StringId}"; @@ -116,10 +116,10 @@ public async Task Cannot_get_secondary_ManyToOne_resource() // Arrange WorkItem workItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{workItem.StringId}/assignee"; @@ -145,10 +145,10 @@ public async Task Cannot_get_secondary_OneToMany_resources() // Arrange UserAccount userAccount = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(userAccount); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/userAccounts/{userAccount.StringId}/assignedItems"; @@ -174,10 +174,10 @@ public async Task Cannot_get_secondary_ManyToMany_resources() // Arrange WorkItem workItem = _fakers.WorkItem.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(workItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/workItems/{workItem.StringId}/tags"; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs index 38b54cf..4c610bf 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemDefinition.cs @@ -17,11 +17,11 @@ public sealed class ImplicitlyChangingWorkItemDefinition(IResourceGraph resource private readonly ReadWriteDbContext _dbContext = dbContext; - public override Task OnWriteSucceededAsync(WorkItem resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) + public override async Task OnWriteSucceededAsync(WorkItem resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) { if (writeOperation is not WriteOperationKind.DeleteResource) { - return _dbContext.WorkItems.ExecuteAsync(async collection => + await _dbContext.WorkItems.ExecuteAsync(async collection => { resource.Description += Suffix; @@ -29,7 +29,5 @@ public override Task OnWriteSucceededAsync(WorkItem resource, WriteOperationKind await collection.ReplaceOneAsync(filter, resource, cancellationToken: cancellationToken); }); } - - return Task.CompletedTask; } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs index ef8c375..570d745 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ImplicitlyChangingWorkItemGroupDefinition.cs @@ -17,11 +17,11 @@ public sealed class ImplicitlyChangingWorkItemGroupDefinition(IResourceGraph res private readonly ReadWriteDbContext _dbContext = dbContext; - public override Task OnWriteSucceededAsync(WorkItemGroup resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) + public override async Task OnWriteSucceededAsync(WorkItemGroup resource, WriteOperationKind writeOperation, CancellationToken cancellationToken) { if (writeOperation is not WriteOperationKind.DeleteResource) { - return _dbContext.Groups.ExecuteAsync(async collection => + await _dbContext.Groups.ExecuteAsync(async collection => { resource.Name += Suffix; @@ -29,7 +29,5 @@ public override Task OnWriteSucceededAsync(WorkItemGroup resource, WriteOperatio await collection.ReplaceOneAsync(filter, resource, cancellationToken: cancellationToken); }); } - - return Task.CompletedTask; } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index 970c893..c2fd820 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_add_to_OneToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); UserAccount existingSubscriber = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingSubscriber); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -70,11 +70,11 @@ public async Task Cannot_add_to_ManyToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); WorkTag existingTag = _fakers.WorkTag.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkTags.Add(existingTag); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index 22dd312..eab8111 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_remove_from_OneToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); existingWorkItem.Subscribers = _fakers.UserAccount.Generate(1).ToHashSet(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingWorkItem.Subscribers.ElementAt(0)); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -70,11 +70,11 @@ public async Task Cannot_remove_from_ManyToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); existingWorkItem.Tags = _fakers.WorkTag.Generate(1).ToHashSet(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkTags.Add(existingWorkItem.Tags.ElementAt(0)); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index 0d623d7..9df3deb 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_replace_OneToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); UserAccount existingSubscriber = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingSubscriber); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -70,11 +70,11 @@ public async Task Cannot_replace_ManyToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); WorkTag existingTag = _fakers.WorkTag.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkTags.Add(existingTag); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index 38168eb..ddd0f1d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_replace_ToOne_relationship() WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); RgbColor existingColor = _fakers.RgbColor.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingColor); dbContext.Groups.Add(existingGroup); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index fe61fa8..f7fa710 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_replace_OneToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); UserAccount existingSubscriber = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingSubscriber); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -81,11 +81,11 @@ public async Task Cannot_replace_ManyToMany_relationship() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); WorkTag existingTag = _fakers.WorkTag.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkTags.Add(existingTag); dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index 6163c5e..672b836 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -36,10 +36,10 @@ public async Task Can_update_resource_without_attributes_or_relationships() // Arrange UserAccount existingUserAccount = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingUserAccount); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -83,10 +83,10 @@ public async Task Can_partially_update_resource_with_string_ID() WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); string newName = _fakers.WorkItemGroup.Generate().Name; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Groups.Add(existingGroup); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -135,10 +135,10 @@ public async Task Can_completely_update_resource_with_string_ID() RgbColor existingColor = _fakers.RgbColor.Generate(); string newDisplayName = _fakers.RgbColor.Generate().DisplayName; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingColor); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -179,10 +179,10 @@ public async Task Can_update_resource_without_side_effects() UserAccount existingUserAccount = _fakers.UserAccount.Generate(); UserAccount newUserAccount = _fakers.UserAccount.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.UserAccounts.Add(existingUserAccount); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -225,10 +225,10 @@ public async Task Can_update_resource_with_side_effects() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); string newDescription = _fakers.WorkItem.Generate().Description!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new @@ -281,10 +281,10 @@ public async Task Can_update_resource_with_side_effects_with_primary_fieldset() WorkItem existingWorkItem = _fakers.WorkItem.Generate(); string newDescription = _fakers.WorkItem.Generate().Description!; - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.WorkItems.Add(existingWorkItem); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index de4ddbc..c1e99fc 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -27,11 +27,11 @@ public async Task Cannot_create_ToOne_relationship() WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); RgbColor existingColor = _fakers.RgbColor.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.RgbColors.Add(existingColor); dbContext.Groups.Add(existingGroup); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); var requestBody = new diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs index 69bc808..df3364e 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs @@ -300,10 +300,10 @@ public async Task Attribute_inclusion_from_resource_definition_is_applied_for_om Star star = _fakers.Star.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Stars.Add(star); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/stars/{star.StringId}"; @@ -340,10 +340,10 @@ public async Task Attribute_inclusion_from_resource_definition_is_applied_for_fi Star star = _fakers.Star.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Stars.Add(star); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/stars/{star.StringId}?fields[stars]=name,solarRadius"; @@ -381,10 +381,10 @@ public async Task Attribute_exclusion_from_resource_definition_is_applied_for_om Star star = _fakers.Star.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Stars.Add(star); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/stars/{star.StringId}"; @@ -421,10 +421,10 @@ public async Task Attribute_exclusion_from_resource_definition_is_applied_for_fi Star star = _fakers.Star.Generate(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Stars.Add(star); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/stars/{star.StringId}?fields[stars]=name,isVisibleFromEarth"; @@ -558,10 +558,10 @@ public async Task Queryable_parameter_handler_from_resource_definition_is_not_ap Planet planet = _fakers.Planet.Generate(); planet.Moons = _fakers.Moon.Generate(1).ToHashSet(); - await _testContext.RunOnDatabaseAsync(dbContext => + await _testContext.RunOnDatabaseAsync(async dbContext => { dbContext.Planets.Add(planet); - return dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); }); string route = $"/planets/{planet.StringId}/moons?isLargerThanTheSun=false"; diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index 2080a47..50fc3fd 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -22,34 +22,34 @@ static IntegrationTest() ThrottleSemaphore = new SemaphoreSlim(maxConcurrentTestRuns); } - public Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteGetAsync(string requestUrl, + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteGetAsync(string requestUrl, Action? setRequestHeaders = null) { - return ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, setRequestHeaders); } - public Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAsync(string requestUrl, + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAsync(string requestUrl, object requestBody, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) { - return ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); } - public Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAtomicAsync(string requestUrl, + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAtomicAsync(string requestUrl, object requestBody, string contentType = HeaderConstants.AtomicOperationsMediaType, Action? setRequestHeaders = null) { - return ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); } - public Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePatchAsync(string requestUrl, + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePatchAsync(string requestUrl, object requestBody, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) { - return ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, setRequestHeaders); } - public Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteDeleteAsync(string requestUrl, + public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteDeleteAsync(string requestUrl, object? requestBody = null, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) { - return ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, setRequestHeaders); } private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteRequestAsync(HttpMethod method, @@ -110,9 +110,9 @@ static IntegrationTest() } } - public Task InitializeAsync() + public async Task InitializeAsync() { - return ThrottleSemaphore.WaitAsync(); + await ThrottleSemaphore.WaitAsync(); } public virtual Task DisposeAsync() diff --git a/test/TestBuildingBlocks/MongoDbContextShim.cs b/test/TestBuildingBlocks/MongoDbContextShim.cs index abc8942..82356d8 100644 --- a/test/TestBuildingBlocks/MongoDbContextShim.cs +++ b/test/TestBuildingBlocks/MongoDbContextShim.cs @@ -22,10 +22,10 @@ protected MongoDbSetShim Set() return dbSetShim; } - public Task ClearTableAsync() + public async Task ClearTableAsync() where TEntity : IMongoIdentifiable { - return _database.DropCollectionAsync(typeof(TEntity).Name); + await _database.DropCollectionAsync(typeof(TEntity).Name); } public async Task SaveChangesAsync(CancellationToken cancellation = default) diff --git a/test/TestBuildingBlocks/MongoDbSetShim.cs b/test/TestBuildingBlocks/MongoDbSetShim.cs index 16e35d5..182ac6e 100644 --- a/test/TestBuildingBlocks/MongoDbSetShim.cs +++ b/test/TestBuildingBlocks/MongoDbSetShim.cs @@ -57,9 +57,9 @@ internal override async Task PersistAsync(CancellationToken cancellationToken) } } - public Task ExecuteAsync(Func, Task> action) + public async Task ExecuteAsync(Func, Task> action) { - return action(_collection); + await action(_collection); } public async Task FirstWithIdAsync(string? id, CancellationToken cancellationToken = default) @@ -79,13 +79,13 @@ public async Task FirstWithIdAsync(string? id, CancellationToken cancel return await _collection.AsQueryable().FirstOrDefaultAsync(document => Equals(document.Id, id), cancellationToken); } - public Task> ToListAsync(CancellationToken cancellationToken = default) + public async Task> ToListAsync(CancellationToken cancellationToken = default) { - return _collection.AsQueryable().ToListAsync(cancellationToken); + return await _collection.AsQueryable().ToListAsync(cancellationToken); } - public Task> ToListWhereAsync(Expression> predicate, CancellationToken cancellationToken = default) + public async Task> ToListWhereAsync(Expression> predicate, CancellationToken cancellationToken = default) { - return _collection.AsQueryable().Where(predicate).ToListAsync(cancellationToken); + return await _collection.AsQueryable().Where(predicate).ToListAsync(cancellationToken); } } From 949b5b0e40ddab9a4481c5483e9a6494d332c311 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 30 Jun 2024 23:10:45 +0200 Subject: [PATCH 28/45] Sync up with changes in JsonApiDotNetCore since v5.5.0 --- .github/workflows/build.yml | 34 +--------------- Directory.Build.props | 2 +- JsonApiDotNetCore.MongoDb.sln | 3 +- WarningSeverities.DotSettings | 4 +- cleanupcode.ps1 | 6 +-- logo.png | Bin 16356 -> 0 bytes package-icon.png | Bin 0 -> 2905 bytes package-versions.props | 12 +++--- .../Controllers/OperationsController.cs | 3 +- .../Configuration/ResourceGraphExtensions.cs | 38 ++++++++++++++++++ .../ServiceCollectionExtensions.cs | 7 ++++ .../JsonApiDotNetCore.MongoDb.csproj | 6 +-- .../Repositories/IMongoDataAccess.cs | 6 +++ .../Repositories/MongoDataAccess.cs | 8 +++- .../Repositories/MongoModel.cs | 33 --------------- .../Repositories/MongoRepository.cs | 15 +++++-- .../AtomicOperations/OperationsController.cs | 3 +- .../AtomicOperations/OperationsFakers.cs | 14 +++---- .../Transactions/LyricRepository.cs | 2 +- .../IntegrationTests/Meta/MetaFakers.cs | 4 +- .../QueryStrings/QueryStringFakers.cs | 8 ++-- .../ReadWrite/ReadWriteFakers.cs | 12 +++--- .../Reading/MoonDefinition.cs | 2 - .../Reading/UniverseFakers.cs | 8 ++-- .../{FakerContainer.cs => FakerExtensions.cs} | 18 ++++++--- 25 files changed, 129 insertions(+), 119 deletions(-) delete mode 100644 logo.png create mode 100644 package-icon.png create mode 100644 src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs delete mode 100644 src/JsonApiDotNetCore.MongoDb/Repositories/MongoModel.cs rename test/TestBuildingBlocks/{FakerContainer.cs => FakerExtensions.cs} (86%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e10312d..75cb869 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,36 +43,6 @@ jobs: dotnet-version: | 6.0.x 8.0.x - - name: Setup PowerShell (Ubuntu) - if: matrix.os == 'ubuntu-latest' - run: | - dotnet tool install --global PowerShell - - name: Find latest PowerShell version (Windows) - if: matrix.os == 'windows-latest' - shell: pwsh - run: | - $packageName = "powershell" - $outputText = dotnet tool search $packageName --take 1 - $outputLine = ("" + $outputText) - $indexOfVersionLine = $outputLine.IndexOf($packageName) - $latestVersion = $outputLine.substring($indexOfVersionLine + $packageName.length).trim().split(" ")[0].trim() - - Write-Output "Found PowerShell version: $latestVersion" - Write-Output "POWERSHELL_LATEST_VERSION=$latestVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - name: Setup PowerShell (Windows) - if: matrix.os == 'windows-latest' - shell: cmd - run: | - set DOWNLOAD_LINK=https://github.com/PowerShell/PowerShell/releases/download/v%POWERSHELL_LATEST_VERSION%/PowerShell-%POWERSHELL_LATEST_VERSION%-win-x64.msi - set OUTPUT_PATH=%RUNNER_TEMP%\PowerShell-%POWERSHELL_LATEST_VERSION%-win-x64.msi - echo Downloading from: %DOWNLOAD_LINK% to: %OUTPUT_PATH% - curl --location --output %OUTPUT_PATH% %DOWNLOAD_LINK% - msiexec.exe /package %OUTPUT_PATH% /quiet USE_MU=1 ENABLE_MU=1 ADD_PATH=1 DISABLE_TELEMETRY=1 - - name: Setup PowerShell (macOS) - if: matrix.os == 'macos-latest' - run: | - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - brew install --cask powershell - name: Show installed versions shell: pwsh run: | @@ -235,13 +205,13 @@ jobs: $baseCommitHash = git rev-parse HEAD~1 Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request." - dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff + dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --properties:RunAnalyzers=false --jb --verbosity=WARN -f commits -a $headCommitHash -b $baseCommitHash --fail-on-diff --print-diff - name: CleanupCode (on branch) if: github.event_name == 'push' || github.event_name == 'release' shell: pwsh run: | Write-Output "Running code cleanup on all files." - dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN --fail-on-diff --print-diff + dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --properties:RunAnalyzers=false --jb --verbosity=WARN --fail-on-diff --print-diff publish: timeout-minutes: 60 diff --git a/Directory.Build.props b/Directory.Build.props index d13bf29..a30d43f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,6 @@ false $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.5.1 + 5.6.0 diff --git a/JsonApiDotNetCore.MongoDb.sln b/JsonApiDotNetCore.MongoDb.sln index 81b5b78..83efaf6 100644 --- a/JsonApiDotNetCore.MongoDb.sln +++ b/JsonApiDotNetCore.MongoDb.sln @@ -23,11 +23,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore + .github\workflows\build.yml = .github\workflows\build.yml CodingGuidelines.ruleset = CodingGuidelines.ruleset CSharpGuidelinesAnalyzer.config = CSharpGuidelinesAnalyzer.config Directory.Build.props = Directory.Build.props - tests.runsettings = tests.runsettings package-versions.props = package-versions.props + tests.runsettings = tests.runsettings EndProjectSection EndProject Global diff --git a/WarningSeverities.DotSettings b/WarningSeverities.DotSettings index 5c641e6..060df31 100644 --- a/WarningSeverities.DotSettings +++ b/WarningSeverities.DotSettings @@ -69,6 +69,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -122,6 +123,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING @@ -164,7 +166,6 @@ WARNING WARNING WARNING - WARNING WARNING WARNING WARNING @@ -196,6 +197,7 @@ WARNING WARNING WARNING + WARNING WARNING WARNING WARNING diff --git a/cleanupcode.ps1 b/cleanupcode.ps1 index a593bd9..9a86aba 100644 --- a/cleanupcode.ps1 +++ b/cleanupcode.ps1 @@ -28,17 +28,17 @@ if ($revision) { if ($baseCommitHash -eq $headCommitHash) { Write-Output "Running code cleanup on staged/unstaged files." - dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified + dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --properties:RunAnalyzers=false --jb --verbosity=WARN -f staged,modified VerifySuccessExitCode } else { Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash, including staged/unstaged files." - dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash + dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --max-runs=5 --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --properties:RunAnalyzers=false --jb --verbosity=WARN -f staged,modified,commits -a $headCommitHash -b $baseCommitHash VerifySuccessExitCode } } else { Write-Output "Running code cleanup on all files." - dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --verbosity=WARN + dotnet regitlint -s JsonApiDotNetCore.MongoDb.sln --print-command --skip-tool-check --jb --dotnetcoresdk=$(dotnet --version) --jb-profile="JADNC Full Cleanup" --jb --properties:Configuration=Release --jb --properties:RunAnalyzers=false --jb --verbosity=WARN VerifySuccessExitCode } diff --git a/logo.png b/logo.png deleted file mode 100644 index 78f1acd5214eb049971fcd5f81dc6222f7904d20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16356 zcmb7rWmFvDvMm;72=4CggIjQScXuX`;OP4bR<9R~mK_e}mmHfM6RKZ-d`9vHKY*Exhl;2 z%_@Uk3f)Gpi$U*;dpBvL8uymYx!PjgN#R@@cenk?toxq#=P_GKyU%9*L8193#XEJM z5OIQ_qD*j@;LvgUnRI{@OhLN!Xc9VbO(x_K^+2nBF*H;3iJ(xjUli$|5Jf{IK_3Rd z|Gxp-TxN|~zu2V-`2X?a=r)J#pl`n;&|~X*g8xQzLLq=}sRa>n;9<{qS6F)7@Xza* zo)tmt^Q(*0$jHcQy1Mkpx5#swo2ApkgHof(^y<{yEd7oxpAf|f4dqg&v2o>p@W*eD zjL7_mjZIuw(2$jr)5NTU5*T)dmj$;~Y%?d|Q37>`Cut!?bv zVqq1txW%l4cN}0M4Y{_wpUQdSFbGgVW2C0mbsF9q3Hgyk&A>3`818>L4q&XRPINut z8S96AE|vq={an&ggSAFo#&ceb`J(;^8Cm23oGYpwqLR>i-( z-8LuuAORvVs3U1}UFWW-q;t;hPN%;ymXHXMs5IXNGJZGfH^8fC#(@x$kks~4&iB=y z#*zxthoh3J5Y(#s-vfj)A`BxJrDKWHi1k||6n(kTN;SZGUYW)8-dxOrhO2exi8(pU zY;2CPj7&OsYa-^0OH0QdV&QWJ*n8M&x|-B2T#K=cWZ5!qS{>DG#|*Lxa*o-z$U^8y zDed5Et3B@=hwQ)&3g?4~)UK|hH6vu_`n=|@66^k7DuD$x(YPq^4k369{|#@l&TB zga^V3BJY(zS65H89BanMsK5&6xYEnaN*-$RzsD9rukNnVT+_=U-a1X^^-OHkrzGC` zSdz@qqhrZfK5L~5sHL-)}SV^}p695KSl$w|nHdEBU`wqc1+@gC&N-d`Uz=#cfY`yU3z z^BDwA>(ebd)*R{kgS6-UhI>g-k@!!$Rf@PTkcikzo%+8BoPF5X*pM>eSJ}$SN|&w0 zBxNCb+Uy`lUqFGkB}Eua|DkBO#gxdRe*ItM5tNul=!wClC9UDs{^58s9Wk+>KYq_= z>|Yn?UInSCCC@uAdIFw3aKuof@qgFkC*gT+Uw?!HQ7?*+&oQ8?vNqNAWJxyW2~lM- znh$9=3Jrdv-MP#UCA8V)NNuC@obk(}Z5LrLUmVBvaP2wshRClCXP8$cW01-PBFq)j zTz=6*D6Ucb)00>G<9?PUWOwxSdv<3B%gD37+epSvxMzgl)XkA+qX(PCogoV##pFS! z+3zWrBhs@?fuEE=DqIp=Loo>f*&E%A@jICEl3 z%AzfGB0vH8^hrD%1)Q~c2>`Rn^6>Czo0*$aGchUTxdUW2=FBdHi$nJ^vUdz)`iq8~ zBLc9ounwI~rn33?FKnF6fyyA!o=a1uJfWKFe+IGhrHb=_iz#<|OX=4uBLiz7|K70m zaq{)T^Jw%znqFS6r%zt+V4%vs%;gCAc6@pG8_e${*<;xM z{OwPh>f62lyLOqZF5AD7wlUmGeJ~u_ADZ9HCuUP^j+5hnsGDK*9 z*f=&Zv2dZhp^*M@$DS7JWx~*G0xMlCNL)d|Sh6*DI(y2Q?BTmuO{CSY;zgZfhK^iA`mwxtewG(Un z48JJ0hQq8E5|fWgeYugxhlOBQ`IlJx9?nzim(6qD`y**qW&F8!GH6AE`bfORFRi!& zXr&%heBlm|{JdVOm}M7*%er*$j0(P7yo*vpDdJxtuc=a1Imhm}uYKmoDfbi#zpIFG(j;knpa)ea7{g|AB0esD| z^}PScg(Kb<-DP>APoz&X;kz7pkI{cTc4G9L9g#CbmVDpeUiH>1;Eximd4sr$E#A@Hmw5^@pd)s66*FA4PAgS@(+1YX3 zFo`r5+TYtddUDd{+08mgO4Dt!8a z_L2s1;!T&_e(|~~SA10F)J6CS9b9Qg*$uwWZZ)B@c?Uix`df3BL)Y!$!P8@1O%-7} zwj8y4f*w5Xf4emYz~j@?%eRq(`4cVY?oze|71#}~8C662!olJRe@Y2jNnEQY!0R9H zgsSl&sJs;Vs&S2k)w>Cf=|j$TqsKw+JulwcQvsNgejfjPiYFVGi#@wD5H(tB((^tQ;0M z$iaJQIqQYQF476T*f%<_EWAO?1e1*ENe+}~H}XDf;Xi~MakdvS>&>UPdp~6C!axcH zUyv~r!_7O^Dl0>v&4*)MZQa^nuA!6QRaW8`=6xle(3}FCYxE}Nf$*Z6o(IIl=aM3z zB&`~~y@bC|g}%F#q_Z$^$uvu)*|ux&Bcw3aPz3`_r<1##r;X#yx;9YrA|Jv0gk4KtSXyz0Dk&S)uw0Vx>?4=AHwpPH9? z(4uXlPiEvae$IaF4Ehy@6wswEQ`;v^R)|pU@xsy^*&0c)CW-j@l2B$KBBDL$gYgJ-sM<({QDA8?KcPsgHa!5T?W9%8Z!V z1t7Kd_aGh>MR{+7a0Ig_fi1rebH?hyJSQ$7NFKqdS4rg;R7BDh1&if);mK>b17NES5>7Xa{ zpQK&Ef6jfQ9f3;!7sNVX<3r(i6IEl_sdb-VieZ*HTPC@M@D}eJ3iX0k1Od@r!5Y*# z0>+>r1Qx|9_bryg)s8d^ zM{j&jPoQPLmmN~~`WwM*iN6@MRzMtNpprL|rc^5Q@ra&gcZYWR~{_At^ zd*{ow@Ou+V)()WSw`Ij*%>Zm*79DTp#d_{Fw<9|!CNhXDlvvC!(L8ntU6kiE(T(~@Bcp;JBC zzd_P^4{eCh`QdVc`icwUki&h~e}04bzu)^q`e0E<+& z8NZZ{V*SEQ2??zyljwcfV`XJ?y_7^!D+r8s^w<yEHibY*g%xJ zNrnOtAEkHe0F(u9lTr41Zo4=>;PLVN>VG47{#~_07k@KobK=~>cxCItPKmLZz zUwhiWUBkyI$SNu&Vp+1_i-bYqh?j#l78deJ(QNRn^z8E~@TbEtr|ICJ_##HoKLtBP zUz#|u?wpWeACwc>d1iY(&1MJc+N9g1M}_?Hh0er)=7b=(!_T|iVeh8pqfyF2scKDt zgNT#y&Ec-JuO{i#Vfb>7344?92e+O#@BgF{O0Y>CJiro2Oo#<|$_X!vWGymjdvw&R zJYOr0Df1kLG7Sz=)3slJXZN09tGqg{s!i-VnkW9!^4=?wfkqokteLXwpT0!s6@`T_ z!iyLG6{*DW6RUFQ%sPv&1Oba6E>z@+y5<23Hs-N19_bE>;f z$;_`tQp5a#gagYYbn$?&9AZ8fndPP{yEmmAen}h_JqpK$&3ltenR>cgwWG@VI-O>j z43)bc5|XvqRch)IY~QabUH$cs-mts<(a`B=o4_A^guY4vH!-QRP`gDX@Ljl8GzpUS zp5$Jm5f~y{Q^-7j@~;n2iU*sq{7rWQd!-IKcpPdR+h7lnlTZTL#Qt@IfkR3JMM41y z+(#<*I)GdPv>iQQr2RpR!veH#)(tZZJVv~47E9@sj}9v{(uBP?LFt0QK)5% zY_nFhW|z=0!s*j9!P9$A+iiDu;sY|AKnfV?AwKJGR(}o@2lDxheQz}twcMv+vdent z^dXX-><(GyaLQzcl)`^V(}zeA_x`ct6lW#l(SC3XSB#m*DJEp18A#{}ktQ8b(ua~V zCj?%#1#I!4ko1`^Z+JVvjh1t)&r~my_I^df5a_3V-t54o{x5|kaOt!z)g;1xFBnY` zoLAeRAHY}2KD4BBC~^4?*dyLJslL<08dE5nPv$gG_epu1eRI>#sHC&VaL{=%#~1C+ z4_Sht8IS=V?ib0Y9=x1$Ij>FZn&+mxR|R)5!oU3DS>%6q=e6^%{4aw^ZH$+(zr=Yx z#l1hEo~=W2*t@T%1O)Zq1NgbCVMio)+j>>vQiqoANT}0tbIIApTI6wAMucry58FNLe0bMT>?0~gJcmPCKS4r zou}B70)pcbXBQ|9+tt9Lvkr1Kz-HcZB^7GnqPw6Z2zykS%VjCejXbgk=_cVEV zP(7N8$9{%&2f{%2Zj$@H`?+M(oEPS5QBj(;<0VUgRESHA_ej&V$l89Y)@p-uCnx=T zafqa069sKYz#fyk z^0ZFN*OVPQWjfQQ4@6`5L{O`_Z+2fSB#D+ar{wij+Q`W0o=Pk>Bm{2#0~=YZuZ`|- z;!#WdG=6_t_WG4ysp6Z*4!GGBnxi^PPftJROaZNtc>IOV!jO{wj9V~b(|PuD{R62)rcKI(VpN2msB(U>;UFYcVi}qUUaIC3m+L`-NB?o((ZZq2N zOskNdzop6LuraSr2P;Xle&74WGAB)z1MaSP!7l=>L>DjFDM z=dxQQliw3et0TL)Z>Z-bKYoDrW4FqfxtR^Hmw=2s7Zyp`sfSp8X%&*evQK~rqNPL{ zaO3SzT6m6`$qE`>OnW|fgd}Ii{f}e#`Fei_PjfV*jcSI&kGSD-G(AZ@ljVA$8kRsz z1zahiwk#4+J#7QWP;MUZ2ELXgqh0gw%F`BZ_NPDTo1#7SNO)*b`x1nN_YQ)9i#TqP zV6=3{JAj9h)SV63PAyYugA^T|27&&F-E6mHQNb@=V{}EF6AGHphlHXT-h*YIvk7tL zC7HN}M;=iH5T+~x;7sE54BreG94IGf{=5XeUwC~DrS}3EXIKO(TPn7%gX-K$fA< zynO7(lHlQjuBJHE(u;S)?*~}-$XIuiUd~n;#IsBToa51)6Wm>VQ7mP&5iTTlpfm7K zWZM%_rm3D}o|0>3`!4MWP~9K*1xIOPNUe%RL9n|BL9=M45D3c~98Ew4PWQIgXSj6& zQ%f&i@f-u=%vePq!dht#GzkYlk!xY_g6l3^s4}B+;Ik#8Hn$lsj7$?yXNYUzYPua7R`iH46kx%=peQ^!@qsMW&p^vuU1GXR(HRY{h#K~$#oaF zXej*d#8Cx{PqL?Mfw+};XrxFK_}9%2GmiW}U>fQXMWJA@B-U+?beaOuFBHm~O@wAL zV&nFXVcKAGMm{jKM;&563XTHquF1lu8hrKnAK6z5r{84;``sy=n|&1SoZ*@CoST_6 z97Ht>&w>=!);o$DY9)zl=5&f;pR~YWo3y1=lw18W7gq*Mc`nEV#^*R-(nz_(kI0z# zw%>F=lp|FE{zcSEugL1XO`ZlIA39?Z*fBeJiVz9I3ZCPPr;=PY#27N{S&32{(^(z3 z6YKO`@A`gSB?6AX5@0z}5TJTqL?xIWGbBZPa(izhkm2i*N(lISJ!VB=vFc}(yfkOp z%Hm_6q6JMZS=%ItB341U2@dF=(eN3;0`}r3f2puIjSxcm*@lF~WE{qdA`B{PxlD!Z z6l(k;OllTBJu}QgDvCs4PL4xAjs+y@U6S&1KavyvU-u5v_)i_w(danh2__fcHfgMt ziKyrKw?rv*N`g&VUdhs-&r4WCgK?12Bn$J&_2~lKHQsNwXi8ET=^szsGZS<({K z7muBjG_aO!V@iez;qWwQ37CBq3X81<#}8#I*d3)xi!G?>{!!B@4*wH_*9}iKLB&Q} z^gEhtE$L|f-C!YAj}SKGh*Z97I-bUw(A9-s83aF!go zO+%$*szuh>Hwz-2!Z#6$0*#7OP8r;_oj{qwSJ9loPCu-qlyWuCZZVgwDz{!@i^(xz z`6`Tf*VZ4!+Rfz3wb+qMH=V(8Z82L&hF0BBqPb+hLUs^!TGa2kQmzVi+KFkkIWzA+ zSH{NG81GRmv)mfuh}6NJTOA`HcB5a0sRQ8=4BW0h*r3mDv-?SfO&D?;?K6|I7SWcv zWhLMwt5sB_v~GGBW~R`~x0@zT_u9oyWC|LiPu#z$+)sAv={*omyj0l!W;#U(yx-BlC_OZ){M6#1pW{EwA;x zQ6VFXIZFFrRL4?@hQS}D_gmXS5y!*k%nDuBPrHPFg%sWR{1K6n$(HhIGUpGnexKrf zDDZ4CiAIHK)?2?fEJwr69v5KJM5l%PwirAH$tpxFlRu2mJET1KBHxYp&;Hj^Yy{1-KM3ao!!|nHaaJrhEm+13LQ{xZ!;tQm>#>hx>z!7 z1u=Y;u{>rsi^;62?a`Z?<9Mkw&I1m?9%EPOqJ@ftI|?2=A{9MmB@!`?r!uGAM9XW3 zBY!f1*GlCc;V!B9+2v+17OA7wr4EL^MjIovI~HzUF25ReDw(sQd2jYZg_#g-;cjPBE?Ji7!(^g zPs)uFaMhn2d?K`AQt0=U)9=2&4_5Q&QtJR}J?MP5g1eQ!q~u{0*V^Qg->*?gm18iy zpP?Xt6L7jIKV55A_jgORJx+sdH|l=EkyYs6ZC})WkOZY0s_D^$qmaa}WfFkDeN)e1 zU-GFBCF=KJ6*~g(%(IYEGJeltna7Py2}w?m2OAh5 zz~4aaMgV{Oh)MY4Xim*)pZS}X>h>t-l>*Ns55$k4pP5xsS*e#$Eg8;9u47@*X5>mM z)c>*r?Bq{j_A8XzG-^^tv7=938>2B|D{e`p#jEj>lgggZdRvXTpGs*^pL3yYTn(I~ zt1FME=x$vfj9J6P#7G{JOAL8{L(sN3vF-j4O<}P*n)tay^sI2iHY-XB6n}R6iWxm^ zpj0r}eW_q303wDI~!$Ui%NPLoZP^K1A`VNaBnY5Kux>fs0podH(XHmvsK>v4 zmOxcy^9ME&{oBN-e46I{RMO+J!h$&k^Ua5H9BO5W(et|e3RzNrE-v`FNRdJcYF1wgcvwIm<1>2shQ2%?F4Kj|1jf!WqpPH8##~b*n zqa_+-Di10QDT_@WAMf3&7`QadNrSgzdI(Q2rL?$9baXng#vT2gk%58p{n2tD(IlN~ z_zr8_*uO*fBiGGmPC&$;QDGcyYGs`pchCI|8PpFf!(UVw?eVnWf_0+Dcs(z>zB~@x zdP#&yR{sr1c)Iud7D^)VCA(|@i7-f%MXHx{Rh5dBzUlI&fPI_^iG4^xNi*|QE~CXG z8)XM|c*t&rlQ}(*_AX`Q#D9|#`{1}mJ$BlJTt?-a_SNWD&$~4Jy_uh4rk~#&e%F)X z?J@|e7jZ7L@c9q*kUDxuwEh0w)K|JgffyKCbjHAML_Pg()rmz?wV?IeL3|`CjXhA4 zT6-)>*ogK(d41Us&Mh$%oETHjsjJfO)6NxV7tisC-60f)DVAx%^h3+*r`D#2Qydnq z0aY5bVxvsS=)Yu|CK;SBbc3!w7oMKyY5FddJlUMOGMLrv4TjuZE)o!T>=QXRKqn1h zg)h79Yc5&fPx+Mhf^U+L<2*Nc!xsOn!Yq7pMm46D_Ptx5)!XlZPW9dw2WF9CaG6X( zV+4U{5;KyG2e0alk10i7G!G6UG8oaS1kXIV3HNydOf{1|npAKWmtPgSunm!L=*+F? zx?XRDR%;@-i72ATr9r3OrP&~l)`B5nbvS5~qa&NMBt9iQARXP#F@(l*yLEln&d+n? z*_>RuJ}gs9x9+7D@=Ncv8+Uw$`E+)$@$C&)GI|6k`GO*!z zzQ4wL?{dj`?8PDb6xPPl)g{4?ecS-)Go5B?*MLUxrWiK4WPRg5e%<>&{Dz;Jo8EWv$^6wZDiShiNSH+}?czq>KCPA2{2O1MYzaDo3q}hMqkm2e z=KrbRV!PL2gBjas*DGh&!)j-PzP(15fun+xwbh+f08R$?9=$>-h_mm?luZl`fl%Y z6w!6LPF(3kMEeXJ84LRd*1d}46l?`p&N#S&i~MqJ`5Z}uwe$(s2h+ex3NH#WKT?w? z9?m1EP=vgB*E_u2&)Zq~C|Sr#>64V#iL-S64$zbi$~bx1%}g4VR+^06)7W$Bxciy@ z_~Y~b60ego)(lrgSCK^fZri@cLAn_7N*+n<&t!D19qS#Q?MLIo??+g2n=<|1} zE8*vVRZja3?QaUP+WvOA2L97TKO!K$whKLf69C4Ezg{+R2@1cg3P1I~_oy~HP4xbV zphqT>GR+gxVA(r3CboV;O~s8;j!rFZULhGiM5t)=Sk#bJaP2i?{#9r#_1rcj?V=De zhjW#fXex}6XzsJ-j5vAhW9*Oebc;lG65WZUvDQu8Q*AtL6-HfFx%ZHZA;|da6hz&> zrq561uiP+4(BHa<{+1EtWYOP}L~P9xN|V9n<`^KCoZ3?t&d9rW&aWKK@#ppY=#ulO z1%S1!pS8Akc<#*KXycYXC&9$8O0N9k&`mOc6VavqNI&-8aj}ife#?HpzGapu^tyxW zBnMWq7~C+Im43Dut8=nuF{3c@UbcHI3r_{J2bp_E?eQ3UtA8EKx^Z_G;i&Df&;9e( zK8R7mNtW*&tAJct zW4E-`y*)}=!LR<7*UDc+)a0DB=RBs6eQ4I0gchVH)B(=|BU=xvKQ%_$^IGdtY5f>g8us%XKHx=a zC`3LPa@Lh%q3721=Y2AT9sJYq5J9K_>Q1)q9wt{l<4Mgj`B}QBL!q|Qx^^d; z&y*iCLFK&X)_OOnw)`z=925A8OHvijZ5a#0pHVhN%6y#~msSs)9+b+xi9p>;`qbpI zN|`x(cnZIBv0ZIA%DVpVDG{eB!H|n%U_L$g;M{9D*xR>;oq+!5*_jQ;{TxgwrOwsS zSNboiB~$~B6f!2?P?~@Lcs*#LhXblGb(lt?_{>1OgBpgUPeJ%s=+SPm5X-<7XIj8_ zG*OTVLy&1{gV=%yQ#uUW3`UmmWE%h8#i6rYqLi{s0(I4uw$z;*du_|o!=XR~&60L2 z?+|S!Po@n+km}!00Qp<5aM5rIpx){4o@*x&O?DbYvV|^_J*saEUcnm%%^y~n8d(b! zKBq+j)3QbEij$w`(U88WYOay(4c_WG1S9A6nIDR5KJ7cwGgdBo{#|uMm~}b}KNu-D zxF9YVA|O?bpKyVH{2K(1BHY$f*{*GCN;kl%sRUR10Yy;d32f?r^grRE%FE9WK3aVg zsWYl!c7vj#OMfc+~5W~S>M^WGn?087L}V&LA*Y8NlCD}{2NqhNgGU( z%SFe0qy50qdCQB9;Q`Bd%8w?FTX%cVgyd6V5OF>N^^8Zs;6%q_G8nkDwnn~qkOe>V z?gYJS0zL1_N|ZR)w75}fwpI?;e*7~7BMWb@tZ;fE#dcSeIS9IcDz7FbG7-M6^x*>x78fe< zbyNf8AKdB#GCn-SKf}a}h(<}P65P8iSZt0q(=M#$qFP zPSj7?{5mxDwX3w>nD1I%K-d`robI0A>jujI1j>fq)g-+ny&NGr@Al?)YLj?AnhTL` zy<8h63J7jklr8~}?0WMFR))yc-{1Nq2%@3FcgD$r*<1y-_~;)y<` zFF|QohYyD~$Jw%$Nrz7qO!*$E756ZcInjzvst`^uq*Q-I! zcMg7}G06-v;i|m->Z(A9FK4oUw!{;AguVs;nBmv^vV@-~*#CY@HDeL|E6$49hjrhq zzyBIC&)UT#{B|nrBShl+z${3-#nMOFS|uq&6UcG0)!fm-7^uEpmC@o zEpFJ9>%p_oD7RYBitc72P#woEgB^s;Qf29I^8jE}<2Pwyu#m#)1~hOdT~bjy@T8Y4omDS*FRnSy&V?OtnVkO-F}RdWMjrs75yH zuH{c^h%DtsG|w z&ddRUX`NC;q!;9VG+B1%raT4bD9hG(6U>o3idWTpUsBb7E>KZPFeImdoAuF3dZXBV ze<~`;?cGbv7CP!IHG0Z8eDua#6Qf8+hw}vQj5a~V%wu83i%NMOcg}K-;_>?<_~-OX zPel74Fnnjt28feu@VVOPfl^qLOqPfRc#ic)i{@&cn>}tq2RaoWWE0a|V3Y`w-XhLU zeS`36=T=A-XvRE}Au<{z3EdZ{NtkKY#}(#fpz0TTYM`I*ZUoJV!GW_k(mXExEa?W!?TsjT{C#@V$fv3BKxe zpXTv{33D(5x z4iD-~npV}^x9Vk&Hp^oAxvVR-jDvlMYUIGLS*~isSf@J!m4UF~IGb5#*pNN$XHG-zbBz#wu zt9n0j!DQV5V~qJ5@*X_mDTpBA%o#vnx~1yiLo!FlYd|A8%s{;L1s0?6n!3rDLyFN^ zEW;n!wWiU39>2Rz$9ugbD+AW*B~H!x6=OUW*~pc5h~hqCx{XR+xr@ z(Z!FPlOb54OAfhqm)li zMzE?j8I;* zq^_G>6G^VNy%deNGnM@3AkYhj3U>DlPD?a688Y2oNK~#y$`}S{rIx;?cpfH3(SG|2Az0B zy^U9B$xaPY+Y|IvNUU$@!Z*z{XiBHe+H7|t)4xQHXZpgfVD>{IlS(C^mlz$B=7a5F ztNCF|5JXNkIySoplKrc`H_t+KQ{zhNvmCxt2pwXyBMuOnXb1vXCZ{A7hf5>h0JBB< zIZeGjRN*cE2FVX{%7zk&U`S4VMN>@vFGQ?U+AHZxd$YC+a|{cp9ZjIfTi zcNynAD{shhf@wn5$s0KK&vVP(S{x^(f+VQkdxPHdxOe%4%}*TNCal`=htuj}JNwBP z(V4(;Ny|4E+{4_y=JfOkxDrphL@M{WVh~yDIH= zdJmPLKOo0~cy8&>(K;{#8qSkYfKRjM>VY^h-E8w5e;&QMsz~0**Q~YcIt^BQN+J6T ztNQoH8y{-4*~Y!wq{izr7;yRbGx9E*w(>)VGj-0TR^+Dv{V#d_k7&+DQBb)#5~&6cykjieQ z4SYyC@}h430&{yZh%Gh|e+7<`qUe`b%+poYmB3)23H$Z(1~KKpX>cZ``LB+FO}8MGqRmgO_9@M>u2HZ?8Sz<-aVNalAc_YVY&% zY(9+w&IpP$(wDIb+2qCh>4eZ_<-}yw==SD1r0kjqHrF>g{FE%ZltE-Q@kU7-r!nlA`;z?F?H7!r(Y2Y(UH_X}QN%5f- z3u+f%<7>fqlNLIh1hDFFh&3)=2tthG<1MCT_8eUqO6qu)GD-4>iqisK(V1tEJifN| zUtuka(>O++ux6c96XUO83je2;XkwI;Y41^4Te+=XQ`xH^Dwj27(Pmq zr4Tni6awTtM2^`uKrwwxnxK&Gv;ocKFITk8V0k4*Ou~7Pxn0m)tmB4W+SbvEC00*s z4mWH}@q$iwA8J#XICLrXhp6EvRZ6jHF`UsKjPkkMKV9oB$i_xaD;tG>#(wDV6F|7b z^rTTXhJr*i!ckqFT8j0}DZv!E<>S?EHpo1T@JYyOkLJja=Ox`!dvA=3Gr3KHAZ2oNrgVNPw zgEWM3J5D8NVg$>F_g+Ovm6mX3H7(waYlDBmHx2G>ftrb}4+BV#@7U;ZOV$2wNVf4X zENVE&miDklmrZgbCR&tH8mzdV-i$Ay;vX$wf$Se7PyjQJG0~Sk3TAf)*9xw2$`G0R z_BD-3n{~%@eY$hS_MJquD%s>&HSK5z^bo{E`qr5lzkUg`5gkjM=YsrUbsuRu{#$o) z1u^$_S(qiZ8G$d%iw-k6*W7XaOPy@u6=XUz(d$)c5lh_lhQS}FOhUJaxMJX@Sc}sB zJa(HpiFbP zvbjEiiDXO8ACbxQASnim3$GZJn1ZkP;)(~4?<8!NpS)c+5=j#Kl<%-$^dmJ@H24Hm2FQ`N9?i)iqg-YD(G!B zLck>zsWQ|H^cjv`2#1Cjxrxi~E`2>yd5IN&z+k6&+Wq4x3r^+5N2wf|lYVQu>!Z^K zIZy9NQF?&^vw|mD;@9W2ye+b`vmD4x9QTP#5hKQYq2=BNhbtC(5O!Ym>_7pCPgX#P zOKS7w+HbY>oJscvAwoe~zpR@xhw%fMl6UNsy#1N!@ku;}c|(Jy;|uTbU6d1|cyk76RsL?bx*4Xpb5&ot56}P$mvv+if<{np$zVAiifY&YaZX zaRRv-zOibD?0b<5$v+Vh)k3AuMH8bTBi`Tdt>iD$n$#zamL3+-|jUHtpTl=emu z@Gp;UI6h0s!(LJDUN-|J(n^hxD`p3?fP3<``MF}q0xp8uef+!aMJDsTk7dT)l^8m? zB*@`hBjw9;6!RJ>fgLD>UKN3}A7StIFvaCouC1FXH*!mA3!Bgg%~axqlc&u%0nbEA zEwD#Hq-2Lzh+fY2GmZYg_u0ok+BS)7;3igeLygk1^03{Vtv0!g1$=UA0sGr*)-9l;?;SdH$U*!6s9_ z#>V6yaIKKCzd)`&p2Lf6c%NxfbLt)IBf-In?VKMXzns3``qca1fApDn$*<$ahoNk9 zVEi85b!e&WwoXME4`*)|89|R8Gb4_+y~t&DPTS#Kcjsw9`*MgTq^f3~@f}rUoejfs z`!>mP(f2yV9*<;h7v@9MfIUri+Ua(_eJMw)8z6^{Fe9Bm^xTk~QqJI!``+UpxXJ0j zOJ;!}722e7bW55+30}Qg12mu+sO-bJ6z$VMy*7YKF|t=lCA$5~8Kl{u2io;;?scK!g zRR+g^L-sxj`F_ZD1tYrE3Oz%7{qtT7?cGNQ+D3j%ACI7hDZ3`Ew^(HL+lo&{1D*7J zxGvrh97o@m#-tC<6D#e-a8M{lu5||Ul!3#1R;T^$oR8hnDs@qY&vXS72}Ie}(JcCL zB|7%!!y$_DGF`B4TTeE7PGWa`G?n6?{u!~)O> zkUS}ngiWnVKL$ki?RG`_DAEz>P^8E>7n(S^%zew|qF(Tuyg zPp2Ls4#|lzjHAL?8R?dP%R=n+O!4A&0#<|%aeK6#`Exor1g^SYbG$lR^|XkfQ}mEC_3E`#5{nW}VcnW-5o z&A%SF51?a+-4EmD^R57_AzNp^+dCTYf-0JCo7s$cQo*OtUaTLx}qsz12`%!U^m zUyQ(2)!7!^;?X5FFn}l%XqdqfH&HuEeZwU>X?I-tw<%{e$6BLfTfKz~v~Lb0LrtTk z(^gtM@{Y=#KFtdHAgvFKNu8Q8yzTbQGJ&za9*9GwQau^-O|2HFm2ONu%pjZZEnC;=s45e~2^g5!JWWL$^=-?H_&b~Hr6OZB5%nhIxp&Rq>+Y6Nh(V?HoI@e8M|mmS@B$ VPtP;)V=^B~R#Hi#R?H;m{{Y9ir3nB4 diff --git a/package-icon.png b/package-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f95eb770e8420f214e310a782e175fc6d793a3f5 GIT binary patch literal 2905 zcmXX|c|4TcA3n1nvWzT=Vq{5i4RNE?wQn_KZ6w2F%f44+m?2rS4Pj;&GZ)w1VvB5} zgzRmSrKU}bM9Y;W-ruX=J)d))^L&@{obTs+-#^|oJ6lT;Az2{+fQYq~nFG92x7Ick zj6Zm}tMDcmW_3Ce0MUC}3(+51iZJB_nG^?`6vG&f=Y*P<)KH&>TbDu*6l%%xH^~pO&hs{)k#uvzE!jcV`AOkL z`I!tRgL&cfHLoMI3$|q$@kMcNwXgzwB;2y}oMAppF4&c2CovO=wGlQ8 z?_jg7D=wZahiiaI1HB){BvKuW6N7Jp%}Q!A?#B_TVqA(N@tkmL_$gSyrtr&@+$eg` z%pPfBj|{X)wL(!$SvQ%!gU0IxOA%om;l}gX>zZLCL9A#G==f$M@6km&OrMlI`I8=mL6-U|> zUvw;vIbFQ)Zn?2+G}M9xH>D@zB|rRqG(|bsIs&B=p;^le&f+VY!q!`b(AKc+51ARXn4>b;aT;Kei_8*8s z0x;Y4pJfKQZ858et(HT3aL--+44i!Y9s@Ou-SI zB=K1i_GIHv(GrJ^CAX(yGanl9y)-7PSEXv5IHBI?;P_}wfEm|0OONqAcVV&1Fno9+ z-cS!WHvIkbf#Vn-pRx3}!=ob^qYuVPB}RryN+jslN~AuVdc-imIbfXW>C$F%BO`U| zzXp&k7msh>`(5W9t1~ROw7uGVd*kE9uGX-$qV{T)D;bXi47;uRW}WQUl0E$sIy$c> zR`}l<6ZH$lCY1^gA)~s^m=E1u-I?6@%vt7cd`w6*#d(A%|HP0c8cWPIT2?>x&ya^R z;d;(bkLDwBn>BYXU8{o_QB(7-N89Ms9I7<6=h-#zf$|@QjgM5 za^|8pzfl~)FJq?ZwECz_f{{Yd^BRA;lFOojSj#m(S5-lwiEkUX7squO?53usy1F{2 z8Q*jC&5e>pZAQcjXxGies%1O(+AK&5Oa>Dj#B0l^TE+dd|A`SiQ2ROy2}sWN+ls3? zE|$)zsAc!xL-wj$B|hbiI(2${uUauWDgtzSj&mWNVy2yqD|oHluuETIx9UdVtX|sv z7dr)3u;R5fRz$oFYHnBcbr#z1W@rxkeFJq+;*e=#(4fzX-l5!0#)5}@uREc_+|ImN z%eKZ+4RTHOa1-tKkLt$yu~l5 zV2x~wO7`<3C8K|xEH2*&rnYFsdLdaBtl{R%4-uNcFws#VKB~q=nmfyvSt3l)1EuU* zx!QRzYw*kV(Oa1~%fy6n? z_t4xPtfuw~xOjsn+LwWx5rZm5N>1dQ6$A{D-Qp&zN9VKl5@LMJw|`t^7r-SngG9c) zb$l|sZ&F+Zu!Q+h@e76CTi2Ez@h?M}%H-i)s4?jgdl*P8mC0UoBM@F@@<8+s?1b&G z0NeL<8y7^Mv{3p6lzT+NaQY!HE+4lEGMDm^{C4|sqVy?dAzNLG%BTQx33JN6cz{C) zlYDMRd6Lx6v3zyq{o3%X zh-?&>PL)UgMb!~=30^%)RmNOaLn=_wl>*;ZO~>a9(ENy{1kM2}sNL~2wIS~!Qd z{>Dx?#KJaBO_DkbQ4L>Qn zdupglOc^&$yOr|l`j%~zK<{|Jl_;|f{WX1%d_+$t&VaG_EvSWF} zkIpapugIU5L8~A--AOFRj6OOcmRvE*=eU+)O^8f4D#xefgmE4{%;x8s&gW?h^v^Dc zCRlMAP-z7Lf?!?GP}zcOgLSB>w@ADnP({S>Z*F|?|D>*(VAs~QC++CXrGSpZ@~$G-Io4;nbV~fMw}tz%DSXq~2|O=@q!lquRPBnM40nUq zK!46Rac@2u1D4xp2wqIobhb;+01{&@j0DjN?s|>ViA2>M)}!Eg#b0Z${#;#==T!c9 zH}Mo%>0mXX>-kpY?RMd4#qn3;gLqm(JNFAXg5!9+k}=s9pH`uQF{$5Jf76|}d9~0b zOrLD=qNiHwwR@Gn-@56{`dD%@!~4^x?t#Zg=Ko}^Uu~q%%e#!-N3dcw@w9V3%Nh7+ zOe&$RY0BFb^rhwRaOA5i8KpY( zszPF=<4L+$RVXx~oUju*Y!xko0KZFjOS(ph`6Q{3iWIj!Gr>&c)Elew&y&)qm*opnwhneao zj4D|Abl|vrM^EpmDfpLgwK|n$d{U>}iLych6!Kr(eUjMlLI+18HkPQrmt8sBd)m!1 zTbF;iKhl0NX#!(kUEX{+sZ+BU#~c0=Yx3a1vX|n9l+FRpC_sfbHNG*6j;)JFac-v( zA0VSzs&6X|^CV=)-y;J$fs?K^v12^lR^ZGfw(PXRE>mq`=axN0*)L}A1=b!1hF0A% zwKO7J7byfMV=Rs*-yj;Bp7-DUs(Gw68=u+oVt3d1_RZ0O+1Q_7vSzBg;{sQF=haM} f-Ec1``3a#mDzn=r`AXlyzdOL%+}7;62`TY^V^51% literal 0 HcmV?d00001 diff --git a/package-versions.props b/package-versions.props index 6d226f5..cd63e0e 100644 --- a/package-versions.props +++ b/package-versions.props @@ -1,20 +1,20 @@ - 5.5.0 + 5.6.0 2.20.0 - 34.0.* + 35.5.* 6.0.* 1.1.* 6.12.* 2.3.* - 1.3.* - 2.22.* + 2.0.* + 2.27.* 8.0.* - 17.8.* - 2.5.* + 17.10.* + 2.8.* diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs index 82f602d..b748e26 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs @@ -8,4 +8,5 @@ namespace JsonApiDotNetCoreMongoDbExample.Controllers; public sealed class OperationsController( IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, - ITargetedFields targetedFields) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields); + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, + request, targetedFields, operationFilter); diff --git a/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs b/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs new file mode 100644 index 0000000..af84a69 --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using JsonApiDotNetCore.Configuration; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using MongoDB.Bson.Serialization.Attributes; + +namespace JsonApiDotNetCore.MongoDb.Configuration; + +internal static class ResourceGraphExtensions +{ + public static IReadOnlyModel ToEntityModel(this IResourceGraph resourceGraph) + { + var modelBuilder = new ModelBuilder(); + + foreach (ResourceType resourceType in resourceGraph.GetResourceTypes()) + { + IncludeResourceType(resourceType, modelBuilder); + } + + return modelBuilder.Model; + } + + private static void IncludeResourceType(ResourceType resourceType, ModelBuilder builder) + { + EntityTypeBuilder entityTypeBuilder = builder.Entity(resourceType.ClrType); + + foreach (PropertyInfo property in resourceType.ClrType.GetProperties().Where(property => !IsIgnored(property))) + { + entityTypeBuilder.Property(property.PropertyType, property.Name); + } + } + + private static bool IsIgnored(PropertyInfo property) + { + return property.GetCustomAttribute() != null; + } +} diff --git a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs index d39c8c7..48278f0 100644 --- a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.AtomicOperations; using JsonApiDotNetCore.MongoDb.Queries.Internal; using JsonApiDotNetCore.MongoDb.Repositories; @@ -17,6 +18,12 @@ public static class ServiceCollectionExtensions [PublicAPI] public static IServiceCollection AddJsonApiMongoDb(this IServiceCollection services) { + services.TryAddSingleton(serviceProvider => + { + var resourceGraph = serviceProvider.GetRequiredService(); + return resourceGraph.ToEntityModel(); + }); + services.TryAddScoped(); // Replace the built-in implementations from JsonApiDotNetCore. diff --git a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj index 07d7077..354b32a 100644 --- a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj +++ b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj @@ -12,11 +12,11 @@ jsonapi;json:api;dotnet;asp.net;rest;web-api;MongoDB Persistence layer implementation for use of MongoDB in APIs using JsonApiDotNetCore. json-api-dotnet - https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb + https://www.jsonapi.net/ MIT false See https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/releases. - logo.png + package-icon.png PackageReadme.md true true @@ -24,7 +24,7 @@ - + diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/IMongoDataAccess.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/IMongoDataAccess.cs index ff2d348..c0f5852 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/IMongoDataAccess.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/IMongoDataAccess.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore.Metadata; using MongoDB.Driver; namespace JsonApiDotNetCore.MongoDb.Repositories; @@ -7,6 +8,11 @@ namespace JsonApiDotNetCore.MongoDb.Repositories; /// public interface IMongoDataAccess : IAsyncDisposable { + /// + /// Provides access to the entity model, which is built at startup. + /// + IReadOnlyModel EntityModel { get; } + /// /// Provides access to the underlying MongoDB database, which data changes can be applied on. /// diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs index 8db3558..018b0a4 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs @@ -1,3 +1,4 @@ +using Microsoft.EntityFrameworkCore.Metadata; using MongoDB.Driver; namespace JsonApiDotNetCore.MongoDb.Repositories; @@ -5,6 +6,9 @@ namespace JsonApiDotNetCore.MongoDb.Repositories; /// public sealed class MongoDataAccess : IMongoDataAccess { + /// + public IReadOnlyModel EntityModel { get; } + /// public IMongoDatabase MongoDatabase { get; } @@ -14,10 +18,12 @@ public sealed class MongoDataAccess : IMongoDataAccess /// public string? TransactionId => ActiveSession is { IsInTransaction: true } ? ActiveSession.GetHashCode().ToString() : null; - public MongoDataAccess(IMongoDatabase mongoDatabase) + public MongoDataAccess(IReadOnlyModel entityModel, IMongoDatabase mongoDatabase) { + ArgumentGuard.NotNull(entityModel); ArgumentGuard.NotNull(mongoDatabase); + EntityModel = entityModel; MongoDatabase = mongoDatabase; } diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoModel.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoModel.cs deleted file mode 100644 index 336a098..0000000 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using JsonApiDotNetCore.Configuration; -using Microsoft.EntityFrameworkCore.Metadata; -using MongoDB.Bson.Serialization.Attributes; - -namespace JsonApiDotNetCore.MongoDb.Repositories; - -internal sealed class MongoModel : RuntimeModel -{ - public MongoModel(IResourceGraph resourceGraph) - { - ArgumentGuard.NotNull(resourceGraph); - - foreach (ResourceType resourceType in resourceGraph.GetResourceTypes()) - { - RuntimeEntityType entityType = AddEntityType(resourceType.ClrType.FullName!, resourceType.ClrType); - SetEntityProperties(entityType, resourceType); - } - } - - private static void SetEntityProperties(RuntimeEntityType entityType, ResourceType resourceType) - { - foreach (PropertyInfo property in resourceType.ClrType.GetProperties().Where(property => !IsIgnored(property))) - { - entityType.AddProperty(property.Name, property.PropertyType, property); - } - } - - private static bool IsIgnored(PropertyInfo property) - { - return property.GetCustomAttribute() != null; - } -} diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index ffdf9e1..579531c 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; @@ -19,6 +20,12 @@ namespace JsonApiDotNetCore.MongoDb.Repositories; /// /// Implements the foundational Repository layer in the JsonApiDotNetCore architecture that uses MongoDB. /// +/// +/// The resource type. +/// +/// +/// The resource identifier type. +/// [PublicAPI] public class MongoRepository : IResourceRepository, IRepositorySupportsTransaction where TResource : class, IIdentifiable @@ -115,7 +122,7 @@ protected virtual IMongoQueryable ApplyQueryLayer(QueryLayer queryLay source = queryableHandler.Apply(source); } - var context = QueryableBuilderContext.CreateRoot(source, typeof(Queryable), new MongoModel(_resourceGraph), null); + var context = QueryableBuilderContext.CreateRoot(source, typeof(Queryable), _mongoDataAccess.EntityModel, null); Expression expression = _queryableBuilder.ApplyQuery(queryLayer, context); return (IMongoQueryable)source.Provider.CreateQuery(expression); @@ -150,7 +157,7 @@ private void AssertNoRelationshipsInSparseFieldSets() } /// - public virtual Task GetForCreateAsync(Type resourceClrType, TId id, CancellationToken cancellationToken) + public virtual Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) { var resource = (TResource)_resourceFactory.CreateInstance(resourceClrType); resource.Id = id; @@ -224,7 +231,7 @@ await SaveChangesAsync( } /// - public virtual async Task DeleteAsync(TResource? resourceFromDatabase, TId id, CancellationToken cancellationToken) + public virtual async Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TId id, CancellationToken cancellationToken) { TResource placeholderResource = resourceFromDatabase ?? _resourceFactory.CreateInstance(); placeholderResource.Id = id; @@ -259,7 +266,7 @@ public virtual Task SetRelationshipAsync(TResource leftResource, object? rightVa } /// - public virtual Task AddToToManyRelationshipAsync(TResource? leftResource, TId leftId, ISet rightResourceIds, + public virtual Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds, CancellationToken cancellationToken) { throw new UnsupportedRelationshipException(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs index c7ce7f9..d8ec189 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs @@ -9,4 +9,5 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; public sealed class OperationsController( IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, - ITargetedFields targetedFields) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields); + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, + request, targetedFields, operationFilter); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs index 9804a9e..51f3ba8 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; -internal sealed class OperationsFakers : FakerContainer +internal sealed class OperationsFakers { private static readonly Lazy> LazyLanguageIsoCodes = new(() => CultureInfo .GetCultures(CultureTypes.NeutralCultures) @@ -16,32 +16,32 @@ internal sealed class OperationsFakers : FakerContainer .ToArray()); private readonly Lazy> _lazyPlaylistFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(playlist => playlist.Name, faker => faker.Lorem.Sentence())); private readonly Lazy> _lazyMusicTrackFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(musicTrack => musicTrack.Title, faker => faker.Lorem.Word()) .RuleFor(musicTrack => musicTrack.LengthInSeconds, faker => faker.Random.Decimal(3 * 60, 5 * 60)) .RuleFor(musicTrack => musicTrack.Genre, faker => faker.Lorem.Word()) .RuleFor(musicTrack => musicTrack.ReleasedAt, faker => faker.Date.PastOffset().TruncateToWholeMilliseconds())); private readonly Lazy> _lazyLyricFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(lyric => lyric.Text, faker => faker.Lorem.Text()) .RuleFor(lyric => lyric.Format, "LRC")); private readonly Lazy> _lazyTextLanguageFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(textLanguage => textLanguage.IsoCode, faker => faker.PickRandom(LazyLanguageIsoCodes.Value))); private readonly Lazy> _lazyPerformerFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(performer => performer.ArtistName, faker => faker.Name.FullName()) .RuleFor(performer => performer.BornAt, faker => faker.Date.PastOffset().TruncateToWholeMilliseconds())); private readonly Lazy> _lazyRecordCompanyFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(recordCompany => recordCompany.Name, faker => faker.Company.CompanyName()) .RuleFor(recordCompany => recordCompany.CountryOfResidence, faker => faker.Address.Country())); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs index cda8d04..d8ad7e0 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs @@ -20,7 +20,7 @@ public LyricRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder) : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) { - IMongoDataAccess otherDataAccess = new MongoDataAccess(mongoDataAccess.MongoDatabase); + IMongoDataAccess otherDataAccess = new MongoDataAccess(mongoDataAccess.EntityModel, mongoDataAccess.MongoDatabase); var factory = new MongoTransactionFactory(otherDataAccess); _transaction = factory.BeginTransactionAsync(CancellationToken.None).Result; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaFakers.cs index 5f29687..9b33ec2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaFakers.cs @@ -6,10 +6,10 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta; -internal sealed class MetaFakers : FakerContainer +internal sealed class MetaFakers { private readonly Lazy> _lazySupportTicketFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(supportTicket => supportTicket.Description, faker => faker.Lorem.Paragraph())); public Faker SupportTicket => _lazySupportTicketFaker.Value; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringFakers.cs index 3026b37..84e1eee 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringFakers.cs @@ -6,20 +6,20 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; -internal sealed class QueryStringFakers : FakerContainer +internal sealed class QueryStringFakers { private readonly Lazy> _lazyBlogFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(blog => blog.Title, faker => faker.Lorem.Word()) .RuleFor(blog => blog.PlatformName, faker => faker.Company.CompanyName())); private readonly Lazy> _lazyBlogPostFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(blogPost => blogPost.Caption, faker => faker.Lorem.Sentence()) .RuleFor(blogPost => blogPost.Url, faker => faker.Internet.Url())); private readonly Lazy> _lazyWebAccountFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(webAccount => webAccount.UserName, faker => faker.Person.UserName) .RuleFor(webAccount => webAccount.Password, faker => faker.Internet.Password()) .RuleFor(webAccount => webAccount.DisplayName, faker => faker.Person.FullName) diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs index 504b9c3..0c84e48 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteFakers.cs @@ -7,31 +7,31 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; -internal sealed class ReadWriteFakers : FakerContainer +internal sealed class ReadWriteFakers { private readonly Lazy> _lazyWorkItemFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(workItem => workItem.Description, faker => faker.Lorem.Sentence()) .RuleFor(workItem => workItem.DueAt, faker => faker.Date.Future().TruncateToWholeMilliseconds()) .RuleFor(workItem => workItem.Priority, faker => faker.PickRandom())); private readonly Lazy> _lazyWorkTagFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(workTag => workTag.Text, faker => faker.Lorem.Word()) .RuleFor(workTag => workTag.IsBuiltIn, faker => faker.Random.Bool())); private readonly Lazy> _lazyUserAccountFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(userAccount => userAccount.FirstName, faker => faker.Name.FirstName()) .RuleFor(userAccount => userAccount.LastName, faker => faker.Name.LastName())); private readonly Lazy> _lazyWorkItemGroupFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(group => group.Name, faker => faker.Lorem.Word()) .RuleFor(group => group.IsPublic, faker => faker.Random.Bool())); private readonly Lazy> _lazyRgbColorFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(color => color.Id, faker => ObjectId.GenerateNewId(faker.Date.Past()).ToString()) .RuleFor(color => color.DisplayName, faker => faker.Commerce.Color())); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs index e4e8f7a..206db10 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/MoonDefinition.cs @@ -23,8 +23,6 @@ public override QueryStringParameterHandlers OnRegisterQueryableHandlersFo private static IQueryable FilterByRadius(IQueryable source, StringValues parameterValue) { - // Workaround for https://youtrack.jetbrains.com/issue/RSRP-493256/Incorrect-possible-null-assignment - // ReSharper disable once AssignNullToNotNullAttribute bool isFilterOnLargerThan = bool.Parse(parameterValue.ToString()); return isFilterOnLargerThan ? source.Where(moon => moon.SolarRadius > 1m) : source.Where(moon => moon.SolarRadius <= 1m); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseFakers.cs index 61164d4..7684481 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseFakers.cs @@ -6,10 +6,10 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; -internal sealed class UniverseFakers : FakerContainer +internal sealed class UniverseFakers { private readonly Lazy> _lazyStarFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(star => star.Name, faker => faker.Random.Word()) .RuleFor(star => star.Kind, faker => faker.PickRandom()) .RuleFor(star => star.SolarRadius, faker => faker.Random.Decimal(.01M, 1000M)) @@ -17,13 +17,13 @@ internal sealed class UniverseFakers : FakerContainer .RuleFor(star => star.IsVisibleFromEarth, faker => faker.Random.Bool())); private readonly Lazy> _lazyPlanetFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(planet => planet.PublicName, faker => faker.Random.Word()) .RuleFor(planet => planet.HasRingSystem, faker => faker.Random.Bool()) .RuleFor(planet => planet.SolarMass, faker => faker.Random.Decimal(.001M, 100M))); private readonly Lazy> _lazyMoonFaker = new(() => new Faker() - .UseSeed(GetFakerSeed()) + .MakeDeterministic() .RuleFor(moon => moon.Name, faker => faker.Random.Word()) .RuleFor(moon => moon.SolarRadius, faker => faker.Random.Decimal(.01M, 1000M))); diff --git a/test/TestBuildingBlocks/FakerContainer.cs b/test/TestBuildingBlocks/FakerExtensions.cs similarity index 86% rename from test/TestBuildingBlocks/FakerContainer.cs rename to test/TestBuildingBlocks/FakerExtensions.cs index 72f9a05..aff1cd0 100644 --- a/test/TestBuildingBlocks/FakerContainer.cs +++ b/test/TestBuildingBlocks/FakerExtensions.cs @@ -1,21 +1,27 @@ using System.Diagnostics; using System.Reflection; -using Bogus.DataSets; +using Bogus; using FluentAssertions.Extensions; using Xunit; namespace TestBuildingBlocks; -public abstract class FakerContainer +public static class FakerExtensions { - static FakerContainer() + public static Faker MakeDeterministic(this Faker faker) + where T : class { + int seed = GetFakerSeed(); + faker.UseSeed(seed); + // Setting the system DateTime to kind Utc, so that faker calls like PastOffset() don't depend on the system time zone. // See https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.op_implicit?view=net-6.0#remarks - Date.SystemClock = () => 1.January(2020).At(1, 1, 1).AsUtc(); + faker.UseDateTimeReference(1.January(2020).At(1, 1, 1).AsUtc()); + + return faker; } - protected static int GetFakerSeed() + private static int GetFakerSeed() { // The goal here is to have stable data over multiple test runs, but at the same time different data per test case. @@ -34,7 +40,7 @@ private static MethodBase GetTestMethod() if (testMethod == null) { // If called after the first await statement, the test method is no longer on the stack, - // but has been replaced with the compiler-generated async/wait state machine. + // but has been replaced with the compiler-generated async/await state machine. throw new InvalidOperationException("Fakers can only be used from within (the start of) a test method."); } From 245691724f4144b8998d0bc621876eb15a9f2cf1 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Mon, 1 Jul 2024 00:01:17 +0200 Subject: [PATCH 29/45] Increment version to 5.6.1 (used for pre-release builds from ci) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index a30d43f..0e7e56e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,6 @@ false $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.6.0 + 5.6.1 From 6e5fe0640c224cfdc49d5128976dec68ad795d23 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jul 2024 11:07:18 +0000 Subject: [PATCH 30/45] Bump dotnet-reportgenerator-globaltool from 5.3.6 to 5.3.7 (#63) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 869b61a..56c65d5 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.6", + "version": "5.3.7", "commands": [ "reportgenerator" ] From 49ca834a1a1ed89aa2ab61a6465e96aa1846999b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:25:46 +0000 Subject: [PATCH 31/45] Bump regitlint from 6.3.12 to 6.3.13 (#64) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 56c65d5..7bd7eff 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "regitlint": { - "version": "6.3.12", + "version": "6.3.13", "commands": [ "regitlint" ] From 5ef48ed0cac85b116867c7a5d1fb7653570c8a53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 27 Jul 2024 10:34:45 +0000 Subject: [PATCH 32/45] Bump dotnet-reportgenerator-globaltool from 5.3.7 to 5.3.8 (#65) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 7bd7eff..cc2edb9 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.7", + "version": "5.3.8", "commands": [ "reportgenerator" ] From 77db43a28753271250d7c37bb5fcc227a3e7bd4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 02:04:02 +0000 Subject: [PATCH 33/45] Bump jetbrains.resharper.globaltools from 2024.1.4 to 2024.1.6 (#67) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index cc2edb9..c6218b7 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2024.1.4", + "version": "2024.1.6", "commands": [ "jb" ] From 688630645317355c199175340e343c425e5f73ce Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:14:55 +0200 Subject: [PATCH 34/45] Reindent dependabot.yml --- .github/dependabot.yml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 53356ab..8bd1514 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,22 +1,22 @@ version: 2 updates: -- package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - pull-request-branch-name: - separator: "-" -- package-ecosystem: nuget - directory: "/" - schedule: - interval: daily - pull-request-branch-name: - separator: "-" - open-pull-requests-limit: 25 - ignore: - # Block updates to all exposed dependencies of the NuGet packages we produce, as updating them would be a breaking change. - - dependency-name: 'JsonApiDotNetCore*' - - dependency-name: 'MongoDB.Driver*' - # Block major updates of packages that require a matching .NET version. - - dependency-name: 'Microsoft.AspNetCore*' - update-types: ["version-update:semver-major"] + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + pull-request-branch-name: + separator: "-" + - package-ecosystem: nuget + directory: "/" + schedule: + interval: daily + pull-request-branch-name: + separator: "-" + open-pull-requests-limit: 25 + ignore: + # Block updates to all exposed dependencies of the NuGet packages we produce, as updating them would be a breaking change. + - dependency-name: "JsonApiDotNetCore*" + - dependency-name: "MongoDB.Driver*" + # Block major updates of packages that require a matching .NET version. + - dependency-name: "Microsoft.AspNetCore*" + update-types: ["version-update:semver-major"] From 2ab9e1766ff704ad383cfd54307b0909ec4d5dac Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 8 Sep 2024 10:16:16 +0200 Subject: [PATCH 35/45] Add link for lack of relationships support --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e93d08..1a33889 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ with both base classes, but `FreeStringMongoIdentifiable` probably makes the mos ## Limitations -- JSON:API relationships are currently not supported. You can use complex object graphs though, which are stored in a single document. +- JSON:API relationships are [currently not supported](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/issues/73). You can use complex object graphs though, which are stored in a single document. ## Contributing From 4a40d0a0de272000b09b0d0cc635b49c931a40a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 11:23:19 +0000 Subject: [PATCH 36/45] Bump dotnet-reportgenerator-globaltool from 5.3.8 to 5.3.9 (#72) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index c6218b7..1790e5a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.8", + "version": "5.3.9", "commands": [ "reportgenerator" ] From 8b82c26105a12a5e98c5f4759d2e8a37b7705a0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:11:17 +0000 Subject: [PATCH 37/45] Bump dotnet-reportgenerator-globaltool from 5.3.9 to 5.3.10 (#77) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1790e5a..fcd0aee 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.9", + "version": "5.3.10", "commands": [ "reportgenerator" ] From 4605b257eec405028f17845c87828b7a20074db1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2024 07:02:55 +0000 Subject: [PATCH 38/45] Bump dotnet-reportgenerator-globaltool from 5.3.10 to 5.3.11 (#79) --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index fcd0aee..2789e0c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.10", + "version": "5.3.11", "commands": [ "reportgenerator" ] From 69f54ba819a791640833221b31d8399c13808b6f Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 23 Apr 2025 04:03:49 +0200 Subject: [PATCH 39/45] Update to latest version of EphemeralMongo (#94) --- package-versions.props | 8 ++++---- test/TestBuildingBlocks/MongoRunnerProvider.cs | 1 - test/TestBuildingBlocks/TestBuildingBlocks.csproj | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/package-versions.props b/package-versions.props index cd63e0e..f0982cc 100644 --- a/package-versions.props +++ b/package-versions.props @@ -2,18 +2,18 @@ 5.6.0 - 2.20.0 + 2.28.0 35.5.* 6.0.* - 1.1.* + 2.0.* 6.12.* 2.3.* 2.0.* - 2.27.* + 2.28.* 8.0.* - 17.10.* + 17.13.* 2.8.* diff --git a/test/TestBuildingBlocks/MongoRunnerProvider.cs b/test/TestBuildingBlocks/MongoRunnerProvider.cs index f2dc54a..96af01f 100644 --- a/test/TestBuildingBlocks/MongoRunnerProvider.cs +++ b/test/TestBuildingBlocks/MongoRunnerProvider.cs @@ -25,7 +25,6 @@ public IMongoRunner Get() { // Single-node replica set mode is required for transaction support in MongoDB. UseSingleNodeReplicaSet = true, - KillMongoProcessesWhenCurrentProcessExits = true, AdditionalArguments = "--quiet" }; diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 954ef72..d7b0934 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -1,4 +1,4 @@ - + net8.0;net6.0 @@ -13,9 +13,9 @@ - - - + + + From 5eca46771b89a58ffacedf65b150a4578236d3e8 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Fri, 25 Apr 2025 01:30:19 +0200 Subject: [PATCH 40/45] Sync up with changes from JsonApiDotNetCore 5.7.0 --- .config/dotnet-tools.json | 13 +- .editorconfig | 166 ++++++++++--- .github/ISSUE_TEMPLATE/question.md | 2 +- .github/workflows/build.yml | 28 ++- .github/workflows/codeql.yml | 4 +- Build.ps1 | 8 +- CodingGuidelines.ruleset | 48 +++- Directory.Build.props | 48 +++- JsonApiDotNetCore.MongoDb.sln.DotSettings | 35 +-- LICENSE | 1 + PackageReadme.md | 2 +- README.md | 232 +++++++++++------- WarningSeverities.DotSettings | 2 +- package-versions.props | 18 +- .../GettingStarted/GettingStarted.csproj | 2 +- src/Examples/GettingStarted/Program.cs | 24 +- .../Properties/launchSettings.json | 2 +- src/Examples/GettingStarted/README.md | 5 +- .../Controllers/OperationsController.cs | 4 +- .../Definitions/TodoItemDefinition.cs | 23 +- .../JsonApiDotNetCoreMongoDbExample.csproj | 2 +- .../Program.cs | 9 +- .../Properties/launchSettings.json | 2 +- .../ArgumentGuard.cs | 17 -- .../AtomicOperations/MongoTransaction.cs | 2 +- .../MongoTransactionFactory.cs | 2 +- .../Configuration/ResourceGraphExtensions.cs | 2 + .../ServiceCollectionExtensions.cs | 4 +- ...ComparisonInFilterNotSupportedException.cs | 9 +- .../UnsupportedRelationshipException.cs | 9 +- .../JsonApiDotNetCore.MongoDb.csproj | 9 +- .../PolyfillCollectionExtensions.cs | 12 + .../Properties/AssemblyInfo.cs | 3 + .../HideRelationshipsSparseFieldSetCache.cs | 12 +- src/JsonApiDotNetCore.MongoDb/ReadOnlySet.cs | 171 +++++++++++++ .../Repositories/MongoDataAccess.cs | 4 +- .../MongoQueryExpressionValidator.cs | 6 +- .../Repositories/MongoRepository.cs | 48 ++-- ...s => AtomicOperationsCollectionFixture.cs} | 2 +- ...rAtomicOperationsTestsThatChangeOptions.cs | 15 +- .../Creating/AtomicCreateResourceTests.cs | 55 ++--- ...reateResourceWithClientGeneratedIdTests.cs | 18 +- ...eateResourceWithToManyRelationshipTests.cs | 8 +- ...reateResourceWithToOneRelationshipTests.cs | 8 +- .../Deleting/AtomicDeleteResourceTests.cs | 8 +- .../LocalIds/AtomicLocalIdTests.cs | 24 +- .../Meta/AtomicResourceMetaTests.cs | 28 +-- .../Mixed/MaximumOperationsPerRequestTests.cs | 3 +- .../AtomicOperations/OperationsController.cs | 4 +- .../AtomicOperations/OperationsDbContext.cs | 3 +- .../AtomicOperations/OperationsFakers.cs | 4 +- .../Transactions/AtomicRollbackTests.cs | 22 +- .../AtomicTransactionConsistencyTests.cs | 19 +- .../Transactions/LyricRepository.cs | 6 +- .../AtomicAddToToManyRelationshipTests.cs | 16 +- ...AtomicRemoveFromToManyRelationshipTests.cs | 16 +- .../AtomicReplaceToManyRelationshipTests.cs | 16 +- .../AtomicUpdateToOneRelationshipTests.cs | 8 +- .../AtomicReplaceToManyRelationshipTests.cs | 8 +- .../Resources/AtomicUpdateResourceTests.cs | 36 +-- .../AtomicUpdateToOneRelationshipTests.cs | 8 +- .../HitCountingResourceDefinition.cs | 13 +- .../IntegrationTests/Meta/MetaDbContext.cs | 3 +- .../Meta/ResourceMetaTests.cs | 12 +- .../Meta/TopLevelCountTests.cs | 15 +- .../Filtering/FilterDataTypeTests.cs | 121 +++++++-- .../QueryStrings/Filtering/FilterDbContext.cs | 3 +- .../Filtering/FilterDepthTests.cs | 10 +- .../Filtering/FilterOperatorTests.cs | 150 +++++++++-- .../QueryStrings/Filtering/FilterTests.cs | 6 +- .../Filtering/FilterableResource.cs | 16 ++ .../QueryStrings/Includes/IncludeTests.cs | 2 +- .../PaginationWithTotalCountTests.cs | 18 +- .../Pagination/RangeValidationTests.cs | 2 +- .../QueryStrings/QueryStringDbContext.cs | 3 +- .../QueryStrings/Sorting/SortTests.cs | 18 +- .../ResultCapturingRepository.cs | 5 +- .../SparseFieldSets/SparseFieldSetTests.cs | 57 +++-- .../QueryStrings/WebAccount.cs | 2 +- .../ReadWrite/Creating/CreateResourceTests.cs | 26 +- ...reateResourceWithClientGeneratedIdTests.cs | 24 +- ...eateResourceWithToManyRelationshipTests.cs | 4 +- ...reateResourceWithToOneRelationshipTests.cs | 8 +- .../ReadWrite/Deleting/DeleteResourceTests.cs | 8 +- .../Fetching/FetchRelationshipTests.cs | 12 +- .../ReadWrite/Fetching/FetchResourceTests.cs | 40 +-- .../ReadWrite/ReadWriteDbContext.cs | 3 +- .../AddToToManyRelationshipTests.cs | 12 +- .../RemoveFromToManyRelationshipTests.cs | 12 +- .../ReplaceToManyRelationshipTests.cs | 12 +- .../UpdateToOneRelationshipTests.cs | 6 +- .../ReplaceToManyRelationshipTests.cs | 12 +- .../Updating/Resources/UpdateResourceTests.cs | 48 ++-- .../Resources/UpdateToOneRelationshipTests.cs | 6 +- .../IntegrationTests/ReadWrite/WorkItem.cs | 2 +- .../Reading/ResourceDefinitionReadTests.cs | 79 +++--- .../Reading/UniverseDbContext.cs | 3 +- .../JsonApiDotNetCoreMongoDbTests.csproj | 2 +- test/TestBuildingBlocks/FakerExtensions.cs | 28 ++- test/TestBuildingBlocks/FluentExtensions.cs | 45 ++++ .../FluentMetaExtensions.cs | 37 +++ test/TestBuildingBlocks/IntegrationTest.cs | 40 ++- .../IntegrationTestContext.cs | 16 +- test/TestBuildingBlocks/MarkedText.cs | 44 ++++ test/TestBuildingBlocks/MongoDbSetShim.cs | 2 +- .../TestBuildingBlocks/MongoRunnerProvider.cs | 15 +- .../NullabilityAssertionExtensions.cs | 43 ---- .../ObjectAssertionsExtensions.cs | 33 --- .../{ => Properties}/AssemblyInfo.cs | 0 test/TestBuildingBlocks/ResourceTypeFinder.cs | 9 +- .../ServiceCollectionExtensions.cs | 3 + .../TestBuildingBlocks.csproj | 4 +- .../TestControllerProvider.cs | 4 +- tests.runsettings | 6 +- 114 files changed, 1556 insertions(+), 873 deletions(-) delete mode 100644 src/JsonApiDotNetCore.MongoDb/ArgumentGuard.cs create mode 100644 src/JsonApiDotNetCore.MongoDb/PolyfillCollectionExtensions.cs create mode 100644 src/JsonApiDotNetCore.MongoDb/Properties/AssemblyInfo.cs rename src/JsonApiDotNetCore.MongoDb/Queries/{Internal => }/HideRelationshipsSparseFieldSetCache.cs (84%) create mode 100644 src/JsonApiDotNetCore.MongoDb/ReadOnlySet.cs rename test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/{AtomicOperationsTestCollection.cs => AtomicOperationsCollectionFixture.cs} (81%) create mode 100644 test/TestBuildingBlocks/FluentExtensions.cs create mode 100644 test/TestBuildingBlocks/FluentMetaExtensions.cs create mode 100644 test/TestBuildingBlocks/MarkedText.cs delete mode 100644 test/TestBuildingBlocks/NullabilityAssertionExtensions.cs delete mode 100644 test/TestBuildingBlocks/ObjectAssertionsExtensions.cs rename test/TestBuildingBlocks/{ => Properties}/AssemblyInfo.cs (100%) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2789e0c..197960a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,22 +3,25 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2024.1.6", + "version": "2024.3.6", "commands": [ "jb" - ] + ], + "rollForward": false }, "regitlint": { "version": "6.3.13", "commands": [ "regitlint" - ] + ], + "rollForward": false }, "dotnet-reportgenerator-globaltool": { - "version": "5.3.11", + "version": "5.4.5", "commands": [ "reportgenerator" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 5a036d1..2e5c106 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,60 +4,125 @@ root = true [*] indent_style = space indent_size = 4 +tab-width = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[*.{config,csproj,css,js,json,props,ruleset,xslt,html}] +[*.{config,csproj,css,js,json,props,targets,xml,ruleset,xsd,xslt,html,yml,yaml}] indent_size = 2 +tab-width = 2 +max_line_length = 160 + +[*.{cs,cshtml,ascx,aspx}] -[*.{cs}] #### C#/.NET Coding Conventions #### +# Default severity for IDE* analyzers with category 'Style' +# Note: specific rules below use severity silent, because Resharper code cleanup auto-fixes them. +dotnet_analyzer_diagnostic.category-Style.severity = warning + # 'using' directive preferences dotnet_sort_system_directives_first = true -csharp_using_directive_placement = outside_namespace:suggestion +csharp_using_directive_placement = outside_namespace:silent +# IDE0005: Remove unnecessary import +dotnet_diagnostic.IDE0005.severity = silent # Namespace declarations -csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_namespace_declarations = file_scoped:silent +# IDE0160: Use block-scoped namespace +dotnet_diagnostic.IDE0160.severity = silent +# IDE0161: Use file-scoped namespace +dotnet_diagnostic.IDE0161.severity = silent # this. preferences -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# IDE0003: Remove this or Me qualification +dotnet_diagnostic.IDE0003.severity = silent +# IDE0009: Add this or Me qualification +dotnet_diagnostic.IDE0009.severity = silent # Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# IDE0049: Use language keywords instead of framework type names for type references +dotnet_diagnostic.IDE0049.severity = silent # Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion -csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion -csharp_style_pattern_local_over_anonymous_function = false:silent +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = silent +csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:silent +# IDE0036: Order modifiers +dotnet_diagnostic.IDE0036.severity = silent # Expression-level preferences dotnet_style_operator_placement_when_wrapping = end_of_line -dotnet_style_prefer_auto_properties = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_return = true:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +dotnet_style_prefer_auto_properties = true:silent +# IDE0032: Use auto property +dotnet_diagnostic.IDE0032.severity = silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +# IDE0045: Use conditional expression for assignment +dotnet_diagnostic.IDE0045.severity = silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +# IDE0046: Use conditional expression for return +dotnet_diagnostic.IDE0046.severity = silent +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +# IDE0058: Remove unused expression value +dotnet_diagnostic.IDE0058.severity = silent + +# Collection expression preferences (note: partially turned off in Directory.Build.props) +dotnet_style_prefer_collection_expression = when_types_exactly_match # Parameter preferences -dotnet_code_quality_unused_parameters = non_public:suggestion +dotnet_code_quality_unused_parameters = non_public + +# Local functions vs lambdas +csharp_style_prefer_local_over_anonymous_function = false:silent +# IDE0039: Use local function instead of lambda +dotnet_diagnostic.IDE0039.severity = silent # Expression-bodied members -csharp_style_expression_bodied_accessors = true:suggestion -csharp_style_expression_bodied_constructors = false:suggestion -csharp_style_expression_bodied_indexers = true:suggestion -csharp_style_expression_bodied_lambdas = true:suggestion -csharp_style_expression_bodied_local_functions = false:suggestion -csharp_style_expression_bodied_methods = false:suggestion -csharp_style_expression_bodied_operators = false:suggestion -csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_accessors = true:silent +# IDE0027: Use expression body for accessors +dotnet_diagnostic.IDE0027.severity = silent +csharp_style_expression_bodied_constructors = false:silent +# IDE0021: Use expression body for constructors +dotnet_diagnostic.IDE0021.severity = silent +csharp_style_expression_bodied_indexers = true:silent +# IDE0026: Use expression body for indexers +dotnet_diagnostic.IDE0026.severity = silent +csharp_style_expression_bodied_lambdas = true:silent +# IDE0053: Use expression body for lambdas +dotnet_diagnostic.IDE0053.severity = silent +csharp_style_expression_bodied_local_functions = false:silent +# IDE0061: Use expression body for local functions +dotnet_diagnostic.IDE0061.severity = silent +csharp_style_expression_bodied_methods = false:silent +# IDE0022: Use expression body for methods +dotnet_diagnostic.IDE0022.severity = silent +csharp_style_expression_bodied_operators = false:silent +# IDE0023: Use expression body for conversion operators +dotnet_diagnostic.IDE0023.severity = silent +# IDE0024: Use expression body for operators +dotnet_diagnostic.IDE0024.severity = silent +csharp_style_expression_bodied_properties = true:silent +# IDE0025: Use expression body for properties +dotnet_diagnostic.IDE0025.severity = silent + +# Member preferences (these analyzers are unreliable) +# IDE0051: Remove unused private member +dotnet_diagnostic.IDE0051.severity = silent +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = silent # Code-block preferences -csharp_prefer_braces = true:suggestion +csharp_prefer_braces = true:silent +# IDE0011: Add braces +dotnet_diagnostic.IDE0011.severity = silent # Indentation preferences csharp_indent_case_contents_when_block = false @@ -66,19 +131,44 @@ csharp_indent_case_contents_when_block = false csharp_preserve_single_line_statements = false # 'var' usage preferences -csharp_style_var_for_built_in_types = false:none -csharp_style_var_when_type_is_apparent = true:none -csharp_style_var_elsewhere = false:none +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = false:silent +# IDE0007: Use var instead of explicit type +dotnet_diagnostic.IDE0007.severity = silent +# IDE0008: Use explicit type instead of var +dotnet_diagnostic.IDE0008.severity = silent # Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion -dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion - -# Expression value is never used -dotnet_diagnostic.IDE0058.severity = none - -#### Naming Style #### +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent +# IDE0047: Remove unnecessary parentheses +dotnet_diagnostic.IDE0047.severity = silent +# IDE0048: Add parentheses for clarity +dotnet_diagnostic.IDE0048.severity = silent + +# Switch preferences +# IDE0010: Add missing cases to switch statement +dotnet_diagnostic.IDE0010.severity = silent +# IDE0072: Add missing cases to switch expression +dotnet_diagnostic.IDE0072.severity = silent + +# Null check preferences +# IDE0029: Null check can be simplified +dotnet_diagnostic.IDE0029.severity = silent +# IDE0030: Null check can be simplified +dotnet_diagnostic.IDE0030.severity = silent +# IDE0270: Null check can be simplified +dotnet_diagnostic.IDE0270.severity = silent + +# JSON002: Probable JSON string detected +dotnet_diagnostic.JSON002.severity = silent + +# CA1062: Validate arguments of public methods +dotnet_code_quality.CA1062.excluded_symbol_names = Accept|DefaultVisit|Visit*|Apply* + +#### .NET Naming Style #### dotnet_diagnostic.IDE1006.severity = warning diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 2520ad8..fcff5ed 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -8,7 +8,7 @@ assignees: '' --- #### SUMMARY diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75cb869..e6a9569 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ concurrency: cancel-in-progress: true env: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_NOLOGO: true DOTNET_CLI_TELEMETRY_OPTOUT: true jobs: @@ -41,8 +41,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x - 8.0.x + 8.0.* + 9.0.* - name: Show installed versions shell: pwsh run: | @@ -66,14 +66,14 @@ jobs: $versionSuffix = $segments.Length -eq 1 ? '' : $segments[1..$($segments.Length - 1)] -join '-' [xml]$xml = Get-Content Directory.Build.props - $configuredVersionPrefix = $xml.Project.PropertyGroup.JsonApiDotNetCoreMongoDbVersionPrefix | Select-Object -First 1 + $configuredVersionPrefix = $xml.Project.PropertyGroup.VersionPrefix | Select-Object -First 1 if ($configuredVersionPrefix -ne $versionPrefix) { Write-Error "Version prefix from git release tag '$versionPrefix' does not match version prefix '$configuredVersionPrefix' stored in Directory.Build.props." # To recover from this: # - Delete the GitHub release # - Run: git push --delete origin the-invalid-tag-name - # - Adjust JsonApiDotNetCoreVersionPrefix in Directory.Build.props, commit and push + # - Adjust VersionPrefix in Directory.Build.props, commit and push # - Recreate the GitHub release } } @@ -97,7 +97,7 @@ jobs: if: matrix.os == 'ubuntu-latest' env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: fail_ci_if_error: true verbose: true @@ -128,8 +128,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x - 8.0.x + 8.0.* + 9.0.* - name: Git checkout uses: actions/checkout@v4 - name: Restore tools @@ -183,8 +183,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x - 8.0.x + 8.0.* + 9.0.* - name: Git checkout uses: actions/checkout@v4 with: @@ -234,6 +234,14 @@ jobs: run: | dotnet nuget add source --username 'json-api-dotnet' --password "$env:GITHUB_TOKEN" --store-password-in-clear-text --name 'github' 'https://nuget.pkg.github.com/json-api-dotnet/index.json' dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:GITHUB_TOKEN" --source 'github' + - name: Publish to feedz.io + if: github.event_name == 'push' || github.event_name == 'release' + env: + FEEDZ_IO_API_KEY: ${{ secrets.FEEDZ_IO_API_KEY }} + shell: pwsh + run: | + dotnet nuget add source --name 'feedz-io' 'https://f.feedz.io/json-api-dotnet/jsonapidotnetcore/nuget/index.json' + dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:FEEDZ_IO_API_KEY" --source 'feedz-io' - name: Publish to NuGet if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags/v') env: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index eb03757..d3d4db6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,8 +27,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.0.x - 8.0.x + 8.0.* + 9.0.* - name: Git checkout uses: actions/checkout@v4 - name: Initialize CodeQL diff --git a/Build.ps1 b/Build.ps1 index 3abc926..6c6ff9c 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,5 +1,3 @@ -$versionSuffix="pre" - function VerifySuccessExitCode { if ($LastExitCode -ne 0) { throw "Command failed with exit code $LastExitCode." @@ -16,14 +14,14 @@ Remove-Item -Recurse -Force * -Include coverage.cobertura.xml dotnet tool restore VerifySuccessExitCode -dotnet build --configuration Release /p:VersionSuffix=$versionSuffix +dotnet build --configuration Release VerifySuccessExitCode -dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" +dotnet test --no-build --configuration Release --verbosity quiet --collect:"XPlat Code Coverage" VerifySuccessExitCode dotnet reportgenerator -reports:**\coverage.cobertura.xml -targetdir:artifacts\coverage -filefilters:-*.g.cs VerifySuccessExitCode -dotnet pack --no-build --configuration Release --output artifacts/packages /p:VersionSuffix=$versionSuffix +dotnet pack --no-build --configuration Release --output artifacts/packages VerifySuccessExitCode diff --git a/CodingGuidelines.ruleset b/CodingGuidelines.ruleset index e647ad9..b29d742 100644 --- a/CodingGuidelines.ruleset +++ b/CodingGuidelines.ruleset @@ -1,32 +1,54 @@  - + + + - - - - - - + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 0e7e56e..86f7636 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,4 +1,31 @@ + + enable + latest + enable + false + false + true + Recommended + $(MSBuildThisFileDirectory)CodingGuidelines.ruleset + $(MSBuildThisFileDirectory)tests.runsettings + 5.7.1 + pre + direct + + + + + IDE0028;IDE0300;IDE0301;IDE0302;IDE0303;IDE0304;IDE0305;IDE0306 + $(NoWarn);$(UseCollectionExpressionRules) + + $(NoWarn);AV2210 @@ -13,20 +40,17 @@ true + + $(NoWarn);CA1707;CA1062 + + + + $(NoWarn);CA1062 + + - + - - - enable - latest - enable - false - false - $(MSBuildThisFileDirectory)CodingGuidelines.ruleset - $(MSBuildThisFileDirectory)tests.runsettings - 5.6.1 - diff --git a/JsonApiDotNetCore.MongoDb.sln.DotSettings b/JsonApiDotNetCore.MongoDb.sln.DotSettings index 2cd13da..cf02da1 100644 --- a/JsonApiDotNetCore.MongoDb.sln.DotSettings +++ b/JsonApiDotNetCore.MongoDb.sln.DotSettings @@ -1,19 +1,9 @@  - // Use the following placeholders: -// $EXPR$ -- source expression -// $NAME$ -- source name (string literal or 'nameof' expression) -// $MESSAGE$ -- string literal in the form of "$NAME$ != null" -JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); - 199 - 5000 - 99 - 100 - 200 - 1000 - 500 + 5000 + 2000 3000 - 50 False + FD312677-2A62-4B8F-A965-879B059F1755/f:ReadOnlySet.cs SOLUTION True True @@ -81,6 +71,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); SUGGESTION SUGGESTION WARNING + DO_NOT_SHOW WARNING WARNING WARNING @@ -99,6 +90,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); WARNING True SUGGESTION + False <?xml version="1.0" encoding="utf-16"?><Profile name="JADNC Full Cleanup"><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" ArrangeNullCheckingPattern="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSReformatInactiveBranches>True</CSReformatInactiveBranches></Profile> JADNC Full Cleanup Required @@ -137,6 +129,7 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); NEVER False NEVER + False False False NEVER @@ -595,11 +588,12 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); True True True + True True True True True - Replace argument null check using throw expression with Guard clause + Replace argument null check using throw expression with ArgumentNullException.ThrowIfNull True True False @@ -618,13 +612,12 @@ JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($EXPR$); True CSHARP False - Replace argument null check with Guard clause - JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($argument$); + System.ArgumentNullException.ThrowIfNull($argument$); $left$ = $right$; $left$ = $right$ ?? throw new ArgumentNullException(nameof($argument$)); WARNING True - Replace argument == null check with Guard clause + Replace argument == null check with ArgumentNullException.ThrowIfNull True True False @@ -633,8 +626,7 @@ $left$ = $right$; True CSHARP False - Replace argument null check with Guard clause - JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($argument$); + System.ArgumentNullException.ThrowIfNull($argument$); if ($argument$ == null) throw new ArgumentNullException(nameof($argument$)); WARNING True @@ -646,12 +638,11 @@ $left$ = $right$; True CSHARP False - Replace collection null/empty check with extension method $collection$.IsNullOrEmpty() $collection$ == null || !$collection$.Any() WARNING True - Replace argument is null check with Guard clause + Replace argument is null check with ArgumentNullException.ThrowIfNull True True False @@ -660,7 +651,7 @@ $left$ = $right$; True CSHARP False - JsonApiDotNetCore.MongoDb.ArgumentGuard.NotNull($argument$); + System.ArgumentNullException.ThrowIfNull($argument$); if ($argument$ is null) throw new ArgumentNullException(nameof($argument$)); WARNING True diff --git a/LICENSE b/LICENSE index 7bfce65..54cf85b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,5 @@ Copyright (c) 2020 Alvaro Nicoli +Copyright (c) 2021 Bart Koelman MIT License diff --git a/PackageReadme.md b/PackageReadme.md index 6c9ce84..a2889e6 100644 --- a/PackageReadme.md +++ b/PackageReadme.md @@ -1 +1 @@ -Persistence layer implementation for use of [MongoDB](https://www.mongodb.com/) in APIs using [JsonApiDotNetCore](https://www.jsonapi.net/). +[MongoDB](https://www.mongodb.com/) persistence for [JsonApiDotNetCore](https://www.jsonapi.net/), which is a framework for building JSON:API compliant REST APIs using ASP.NET Core. diff --git a/README.md b/README.md index 1a33889..7a0e52d 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,181 @@ # MongoDB support for JsonApiDotNetCore -Plug-n-play implementation of `IResourceRepository` allowing you to use [MongoDB](https://www.mongodb.com/) with your [JsonApiDotNetCore](https://github.com/json-api-dotnet/JsonApiDotNetCore) APIs. - [![Build](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/actions/workflows/build.yml?query=branch%3Amaster) [![Coverage](https://codecov.io/gh/json-api-dotnet/JsonApiDotNetCore.MongoDb/branch/master/graph/badge.svg?token=QPVf8rii7l)](https://codecov.io/gh/json-api-dotnet/JsonApiDotNetCore.MongoDb) [![NuGet](https://img.shields.io/nuget/v/JsonApiDotNetCore.MongoDb.svg)](https://www.nuget.org/packages/JsonApiDotNetCore.MongoDb/) +[![GitHub License](https://img.shields.io/github/license/json-api-dotnet/JsonApiDotNetCore.MongoDb)](LICENSE) -## Installation and Usage - -```bash -dotnet add package JsonApiDotNetCore.MongoDb -``` - -### Models - -```c# -#nullable enable - -[Resource] -public class Book : HexStringMongoIdentifiable -{ - [Attr] - public string Name { get; set; } = null!; -} -``` - -### Middleware - -```c# -// Program.cs +Plug-n-play implementation of `IResourceRepository`, allowing you to use [MongoDB](https://www.mongodb.com/) with your [JsonApiDotNetCore](https://github.com/json-api-dotnet/JsonApiDotNetCore) API projects. -#nullable enable +## Getting started -WebApplicationBuilder builder = WebApplication.CreateBuilder(args); +The following steps describe how to create a JSON:API project with MongoDB. -// Add services to the container. - -builder.Services.AddSingleton(_ => -{ - var client = new MongoClient("mongodb://localhost:27017"); - return client.GetDatabase("ExampleDbName"); -}); - -builder.Services.AddJsonApi(resources: resourceGraphBuilder => -{ - resourceGraphBuilder.Add(); -}); - -builder.Services.AddJsonApiMongoDb(); +1. Install the JsonApiDotNetCore.MongoDb package: + ```bash + dotnet add package JsonApiDotNetCore.MongoDb + ``` -builder.Services.AddResourceRepository>(); +1. Declare your entities, annotated with JsonApiDotNetCore attributes: + ```c# + #nullable enable -// Configure the HTTP request pipeline. + [Resource] + public class Person : HexStringMongoIdentifiable + { + [Attr] public string? FirstName { get; set; } + [Attr] public string LastName { get; set; } = null!; + } + ``` -app.UseRouting(); -app.UseJsonApi(); -app.MapControllers(); +1. Configure MongoDB and JsonApiDotNetCore in `Program.cs`, seeding the database with sample data: + ```c# + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddSingleton(_ => new MongoClient("mongodb://localhost:27017").GetDatabase("ExampleDbName")); + builder.Services.AddJsonApi(options => + { + options.UseRelativeLinks = true; + options.IncludeTotalResourceCount = true; + }, resources: resourceGraphBuilder => resourceGraphBuilder.Add()); + builder.Services.AddJsonApiMongoDb(); + builder.Services.AddResourceRepository>(); + + var app = builder.Build(); + app.UseRouting(); + app.UseJsonApi(); + app.MapControllers(); + + var database = app.Services.GetRequiredService(); + await CreateSampleDataAsync(database); + + app.Run(); + + static async Task CreateSampleDataAsync(IMongoDatabase database) + { + await database.DropCollectionAsync(nameof(Person)); + await database.GetCollection(nameof(Person)).InsertManyAsync(new[] + { + new Person + { + FirstName = "John", + LastName = "Doe", + }, + new Person + { + FirstName = "Jane", + LastName = "Doe", + }, + new Person + { + FirstName = "John", + LastName = "Smith", + } + }); + } + ``` -app.Run(); -``` + > [!TIP] + > If your API project uses MongoDB only (so not in combination with EF Core), then instead of + > registering all MongoDB resources and repositories individually, you can use: + > + > ```c# + > builder.Services.AddJsonApi(facade => facade.AddCurrentAssembly()); + > builder.Services.AddJsonApiMongoDb(); + > + > builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); + > builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); + > builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); + > ``` + +1. Start your API + ```bash + dotnet run + ``` -Note: If your API project uses MongoDB only (so not in combination with EF Core), then instead of -registering all MongoDB resources and repositories individually, you can use: +1. Send a GET request to retrieve data: + ```bash + GET http://localhost:5000/people?filter=equals(lastName,'Doe')&fields[people]=firstName HTTP/1.1 + ``` -```c# -builder.Services.AddJsonApi(facade => facade.AddCurrentAssembly()); -builder.Services.AddJsonApiMongoDb(); +
+ Expand to view the JSON response + + ```json + { + "links": { + "self": "/people?filter=equals(lastName,%27Doe%27)&fields[people]=firstName", + "first": "/people?filter=equals(lastName,%27Doe%27)&fields%5Bpeople%5D=firstName", + "last": "/people?filter=equals(lastName,%27Doe%27)&fields%5Bpeople%5D=firstName" + }, + "data": [ + { + "type": "people", + "id": "680cae2e1759666c5c1e988c", + "attributes": { + "firstName": "John" + }, + "links": { + "self": "/people/680cae2e1759666c5c1e988c" + } + }, + { + "type": "people", + "id": "680cae2e1759666c5c1e988d", + "attributes": { + "firstName": "Jane" + }, + "links": { + "self": "/people/680cae2e1759666c5c1e988d" + } + } + ], + "meta": { + "total": 2 + } + } + ``` -builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); -builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); -builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); -``` +
## Using client-generated IDs -Resources that inherit from `HexStringMongoIdentifiable` use auto-generated (performant) 12-byte hexadecimal + +Resources that inherit from `HexStringMongoIdentifiable` use auto-generated (high-performance) 12-byte hexadecimal [Object IDs](https://docs.mongodb.com/manual/reference/bson-types/#objectid). -You can assign an ID manually, but it must match the 12-byte hexadecimal pattern. +You can assign an ID explicitly, but it must match the 12-byte hexadecimal pattern. -To assign free-format string IDs manually, make your resources inherit from `FreeStringMongoIdentifiable` instead. +To use free-format string IDs, make your resources inherit from `FreeStringMongoIdentifiable` instead. When creating a resource without assigning an ID, a 12-byte hexadecimal ID will be auto-generated. -Set `options.AllowClientGeneratedIds` to `true` in Program.cs to allow API clients to assign IDs. This can be combined +Set `options.ClientIdGeneration` to `Allowed` or `Required` from `Program.cs` to enable API clients to assign IDs. This can be combined with both base classes, but `FreeStringMongoIdentifiable` probably makes the most sense. ## Limitations -- JSON:API relationships are [currently not supported](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/issues/73). You can use complex object graphs though, which are stored in a single document. - -## Contributing - -Have a question, found a bug or want to submit code changes? See our [contributing guidelines](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/.github/CONTRIBUTING.md). +- JSON:API relationships are [currently not supported](https://github.com/json-api-dotnet/JsonApiDotNetCore.MongoDb/issues/73). You *can* use complex object graphs though, which are stored in a single document. ## Trying out the latest build -After each commit to the master branch, a new pre-release NuGet package is automatically published to [GitHub Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry). +After each commit to the master branch, a new pre-release NuGet package is automatically published to [feedz.io](https://feedz.io/docs/package-types/nuget). To try it out, follow the steps below: -1. [Create a Personal Access Token (classic)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with at least `read:packages` scope. -1. Add our package source to your local user-specific `nuget.config` file by running: - ```bash - dotnet nuget add source https://nuget.pkg.github.com/json-api-dotnet/index.json --name github-json-api --username YOUR-GITHUB-USERNAME --password YOUR-PAT-CLASSIC +1. Create a `nuget.config` file in the same directory as your .sln file, with the following contents: + ```xml + + + + + + + ``` - In the command above: - - Replace YOUR-GITHUB-USERNAME with the username you use to login your GitHub account. - - Replace YOUR-PAT-CLASSIC with the token your created above. - - :warning: If the above command doesn't give you access in the next step, remove the package source by running: - ```bash - dotnet nuget remove source github-json-api - ``` - and retry with the `--store-password-in-clear-text` switch added. -1. Restart your IDE, open your project, and browse the list of packages from the github-json-api feed (make sure pre-release packages are included). -## Development +1. In your IDE, browse the list of packages from the `json-api-dotnet` feed. Make sure pre-release packages are included in the list. + +## Contributing + +Have a question, found a bug or want to submit code changes? See our [contributing guidelines](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/.github/CONTRIBUTING.md). + +## Build from source To build the code from this repository locally, run: @@ -120,13 +183,14 @@ To build the code from this repository locally, run: dotnet build ``` -You don't need to have a running instance of MongoDB on your machine to run tests. Just type the following command in your terminal: +You can run tests without MongoDB on your machine. The following command runs all tests: ```bash dotnet test ``` -If you want to run the examples and explore them on your own **you are** going to need that running instance of MongoDB. If you have docker installed you can launch it like this: +A running instance of MongoDB is required to run the examples. +If you have docker installed, you can launch MongoDB in a container with the following command: ```bash pwsh run-docker-mongodb.ps1 diff --git a/WarningSeverities.DotSettings b/WarningSeverities.DotSettings index 060df31..5b64971 100644 --- a/WarningSeverities.DotSettings +++ b/WarningSeverities.DotSettings @@ -1,4 +1,5 @@  + WARNING WARNING WARNING WARNING @@ -197,7 +198,6 @@ WARNING WARNING WARNING - WARNING WARNING WARNING WARNING diff --git a/package-versions.props b/package-versions.props index f0982cc..14ed6fe 100644 --- a/package-versions.props +++ b/package-versions.props @@ -5,25 +5,25 @@ 2.28.0 - 35.5.* + 35.6.* 6.0.* 2.0.* - 6.12.* - 2.3.* + 7.2.* + 2.4.* 2.0.* 2.28.* - 8.0.* 17.13.* - 2.8.* + 2.9.* + 2.8.* - + - 8.0.* + 9.0.* - + - 6.0.* + 8.0.*
diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj index 0f6b40f..408a434 100644 --- a/src/Examples/GettingStarted/GettingStarted.csproj +++ b/src/Examples/GettingStarted/GettingStarted.csproj @@ -1,6 +1,6 @@ - net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/GettingStarted/Program.cs b/src/Examples/GettingStarted/Program.cs index 7deafcc..3a20376 100644 --- a/src/Examples/GettingStarted/Program.cs +++ b/src/Examples/GettingStarted/Program.cs @@ -15,7 +15,19 @@ return client.GetDatabase(builder.Configuration.GetSection("DatabaseSettings:Database").Value); }); -builder.Services.AddJsonApi(ConfigureJsonApiOptions, resources: resourceGraphBuilder => resourceGraphBuilder.Add()); +builder.Services.AddJsonApi(options => +{ + options.Namespace = "api"; + options.UseRelativeLinks = true; + options.IncludeTotalResourceCount = true; + +#if DEBUG + options.IncludeExceptionStackTraceInErrors = true; + options.IncludeRequestBodyInErrors = true; + options.SerializerOptions.WriteIndented = true; +#endif +}, resources: resourceGraphBuilder => resourceGraphBuilder.Add()); + builder.Services.AddJsonApiMongoDb(); builder.Services.AddResourceRepository>(); @@ -31,15 +43,7 @@ var database = app.Services.GetRequiredService(); await CreateSampleDataAsync(database); -app.Run(); - -static void ConfigureJsonApiOptions(JsonApiOptions options) -{ - options.Namespace = "api"; - options.UseRelativeLinks = true; - options.IncludeTotalResourceCount = true; - options.SerializerOptions.WriteIndented = true; -} +await app.RunAsync(); static async Task CreateSampleDataAsync(IMongoDatabase database) { diff --git a/src/Examples/GettingStarted/Properties/launchSettings.json b/src/Examples/GettingStarted/Properties/launchSettings.json index b82968b..55041b5 100644 --- a/src/Examples/GettingStarted/Properties/launchSettings.json +++ b/src/Examples/GettingStarted/Properties/launchSettings.json @@ -1,5 +1,5 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, diff --git a/src/Examples/GettingStarted/README.md b/src/Examples/GettingStarted/README.md index aa8afe0..b076df4 100644 --- a/src/Examples/GettingStarted/README.md +++ b/src/Examples/GettingStarted/README.md @@ -7,8 +7,7 @@ You can verify the project is running by checking this endpoint: `localhost:24141/api/people` -For further documentation and implementation of a JsonApiDotNetCore Application see the documentation or GitHub page: +For further documentation and implementation of a JsonApiDotNetCore application, see the documentation or GitHub page: Repository: https://github.com/json-api-dotnet/JsonApiDotNetCore - -Documentation: http://www.jsonapi.net +Documentation: https://www.jsonapi.net diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs index b748e26..a2a1ca7 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Controllers/OperationsController.cs @@ -8,5 +8,5 @@ namespace JsonApiDotNetCoreMongoDbExample.Controllers; public sealed class OperationsController( IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, - ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, - request, targetedFields, operationFilter); + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) + : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter); diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs index 7910a8e..96c21ee 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Definitions/TodoItemDefinition.cs @@ -5,27 +5,14 @@ using JsonApiDotNetCore.Queries.Expressions; using JsonApiDotNetCore.Resources; using JsonApiDotNetCoreMongoDbExample.Models; -#if NET6_0 -using Microsoft.AspNetCore.Authentication; -#endif namespace JsonApiDotNetCoreMongoDbExample.Definitions; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] -public sealed class TodoItemDefinition( - IResourceGraph resourceGraph, -#if NET6_0 - ISystemClock systemClock -#else - TimeProvider timeProvider -#endif -) : JsonApiResourceDefinition(resourceGraph) +public sealed class TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider) + : JsonApiResourceDefinition(resourceGraph) { -#if NET6_0 - private readonly Func _getUtcNow = () => systemClock.UtcNow; -#else - private readonly Func _getUtcNow = timeProvider.GetUtcNow; -#endif + private readonly TimeProvider _timeProvider = timeProvider; public override SortExpression OnApplySort(SortExpression? existingSort) { @@ -44,11 +31,11 @@ public override Task OnWritingAsync(TodoItem resource, WriteOperationKind writeO { if (writeOperation == WriteOperationKind.CreateResource) { - resource.CreatedAt = _getUtcNow(); + resource.CreatedAt = _timeProvider.GetUtcNow(); } else if (writeOperation == WriteOperationKind.UpdateResource) { - resource.LastModifiedAt = _getUtcNow(); + resource.LastModifiedAt = _timeProvider.GetUtcNow(); } return Task.CompletedTask; diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/JsonApiDotNetCoreMongoDbExample.csproj b/src/Examples/JsonApiDotNetCoreMongoDbExample/JsonApiDotNetCoreMongoDbExample.csproj index 0f6b40f..408a434 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/JsonApiDotNetCoreMongoDbExample.csproj +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/JsonApiDotNetCoreMongoDbExample.csproj @@ -1,6 +1,6 @@ - net8.0;net6.0 + net9.0;net8.0 diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs b/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs index 95ec278..ff216b8 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Program.cs @@ -6,9 +6,6 @@ using JsonApiDotNetCore.Repositories; using Microsoft.Extensions.DependencyInjection.Extensions; using MongoDB.Driver; -#if NET6_0 -using Microsoft.AspNetCore.Authentication; -#endif [assembly: ExcludeFromCodeCoverage] @@ -16,11 +13,7 @@ // Add services to the container. -#if NET6_0 -builder.Services.TryAddSingleton(); -#else builder.Services.TryAddSingleton(TimeProvider.System); -#endif builder.Services.TryAddSingleton(_ => { @@ -43,7 +36,7 @@ app.UseJsonApi(); app.MapControllers(); -app.Run(); +await app.RunAsync(); static void ConfigureJsonApiOptions(JsonApiOptions options) { diff --git a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json b/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json index c14bdd1..efcce2e 100644 --- a/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json +++ b/src/Examples/JsonApiDotNetCoreMongoDbExample/Properties/launchSettings.json @@ -1,5 +1,5 @@ { - "$schema": "http://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, diff --git a/src/JsonApiDotNetCore.MongoDb/ArgumentGuard.cs b/src/JsonApiDotNetCore.MongoDb/ArgumentGuard.cs deleted file mode 100644 index 5063b63..0000000 --- a/src/JsonApiDotNetCore.MongoDb/ArgumentGuard.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Runtime.CompilerServices; -using JetBrains.Annotations; -using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; - -#pragma warning disable AV1008 // Class should not be static - -namespace JsonApiDotNetCore.MongoDb; - -internal static class ArgumentGuard -{ - [AssertionMethod] - public static void NotNull([NoEnumeration] [SysNotNull] T? value, [CallerArgumentExpression("value")] string? parameterName = null) - where T : class - { - ArgumentNullException.ThrowIfNull(value, parameterName); - } -} diff --git a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs index 3514e87..0e19327 100644 --- a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs +++ b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransaction.cs @@ -16,7 +16,7 @@ public sealed class MongoTransaction : IOperationsTransaction public MongoTransaction(IMongoDataAccess mongoDataAccess, bool ownsTransaction) { - ArgumentGuard.NotNull(mongoDataAccess); + ArgumentNullException.ThrowIfNull(mongoDataAccess); _mongoDataAccess = mongoDataAccess; _ownsTransaction = ownsTransaction; diff --git a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransactionFactory.cs b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransactionFactory.cs index b51e1a1..d016781 100644 --- a/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransactionFactory.cs +++ b/src/JsonApiDotNetCore.MongoDb/AtomicOperations/MongoTransactionFactory.cs @@ -12,7 +12,7 @@ public sealed class MongoTransactionFactory : IOperationsTransactionFactory public MongoTransactionFactory(IMongoDataAccess mongoDataAccess) { - ArgumentGuard.NotNull(mongoDataAccess); + ArgumentNullException.ThrowIfNull(mongoDataAccess); _mongoDataAccess = mongoDataAccess; } diff --git a/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs b/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs index af84a69..5ad31b2 100644 --- a/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs +++ b/src/JsonApiDotNetCore.MongoDb/Configuration/ResourceGraphExtensions.cs @@ -11,6 +11,8 @@ internal static class ResourceGraphExtensions { public static IReadOnlyModel ToEntityModel(this IResourceGraph resourceGraph) { + ArgumentNullException.ThrowIfNull(resourceGraph); + var modelBuilder = new ModelBuilder(); foreach (ResourceType resourceType in resourceGraph.GetResourceTypes()) diff --git a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs index 48278f0..a49d77e 100644 --- a/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore.MongoDb/Configuration/ServiceCollectionExtensions.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.AtomicOperations; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.MongoDb.AtomicOperations; -using JsonApiDotNetCore.MongoDb.Queries.Internal; +using JsonApiDotNetCore.MongoDb.Queries; using JsonApiDotNetCore.MongoDb.Repositories; using JsonApiDotNetCore.Queries; using Microsoft.Extensions.DependencyInjection; @@ -18,6 +18,8 @@ public static class ServiceCollectionExtensions [PublicAPI] public static IServiceCollection AddJsonApiMongoDb(this IServiceCollection services) { + ArgumentNullException.ThrowIfNull(services); + services.TryAddSingleton(serviceProvider => { var resourceGraph = serviceProvider.GetRequiredService(); diff --git a/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs b/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs index 55c865a..51c7131 100644 --- a/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs +++ b/src/JsonApiDotNetCore.MongoDb/Errors/AttributeComparisonInFilterNotSupportedException.cs @@ -10,7 +10,8 @@ namespace JsonApiDotNetCore.MongoDb.Errors; /// https://jira.mongodb.org/browse/CSHARP-1592. /// [PublicAPI] -public sealed class AttributeComparisonInFilterNotSupportedException() : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) -{ - Title = "Comparing attributes against each other is not supported when using MongoDB." -}); +public sealed class AttributeComparisonInFilterNotSupportedException() + : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) + { + Title = "Comparing attributes against each other is not supported when using MongoDB." + }); diff --git a/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs b/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs index 01852e5..b1401c2 100644 --- a/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs +++ b/src/JsonApiDotNetCore.MongoDb/Errors/UnsupportedRelationshipException.cs @@ -9,7 +9,8 @@ namespace JsonApiDotNetCore.MongoDb.Errors; /// The error that is thrown when the user attempts to fetch, create or update a relationship. /// [PublicAPI] -public sealed class UnsupportedRelationshipException() : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) -{ - Title = "Relationships are not supported when using MongoDB." -}); +public sealed class UnsupportedRelationshipException() + : JsonApiException(new ErrorObject(HttpStatusCode.BadRequest) + { + Title = "Relationships are not supported when using MongoDB." + }); diff --git a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj index 354b32a..13aa46a 100644 --- a/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj +++ b/src/JsonApiDotNetCore.MongoDb/JsonApiDotNetCore.MongoDb.csproj @@ -1,6 +1,6 @@ - + - net8.0;net6.0 + net8.0 true true @@ -8,9 +8,8 @@ - $(JsonApiDotNetCoreMongoDbVersionPrefix) jsonapi;json:api;dotnet;asp.net;rest;web-api;MongoDB - Persistence layer implementation for use of MongoDB in APIs using JsonApiDotNetCore. + MongoDB persistence for JsonApiDotNetCore, which is a framework for building JSON:API compliant REST APIs using ASP.NET Core. json-api-dotnet https://www.jsonapi.net/ MIT @@ -19,7 +18,6 @@ package-icon.png PackageReadme.md true - true embedded @@ -34,7 +32,6 @@ - diff --git a/src/JsonApiDotNetCore.MongoDb/PolyfillCollectionExtensions.cs b/src/JsonApiDotNetCore.MongoDb/PolyfillCollectionExtensions.cs new file mode 100644 index 0000000..06adff6 --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/PolyfillCollectionExtensions.cs @@ -0,0 +1,12 @@ +using System.Collections.ObjectModel; + +namespace JsonApiDotNetCore.MongoDb; + +// These methods provide polyfills for lower .NET versions. +internal static class PolyfillCollectionExtensions +{ + public static IReadOnlySet AsReadOnly(this HashSet source) + { + return new ReadOnlySet(source); + } +} diff --git a/src/JsonApiDotNetCore.MongoDb/Properties/AssemblyInfo.cs b/src/JsonApiDotNetCore.MongoDb/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..58b7ea5 --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("TestBuildingBlocks")] diff --git a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs b/src/JsonApiDotNetCore.MongoDb/Queries/HideRelationshipsSparseFieldSetCache.cs similarity index 84% rename from src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs rename to src/JsonApiDotNetCore.MongoDb/Queries/HideRelationshipsSparseFieldSetCache.cs index aeebe64..ac5f45c 100644 --- a/src/JsonApiDotNetCore.MongoDb/Queries/Internal/HideRelationshipsSparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore.MongoDb/Queries/HideRelationshipsSparseFieldSetCache.cs @@ -5,7 +5,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace JsonApiDotNetCore.MongoDb.Queries.Internal; +namespace JsonApiDotNetCore.MongoDb.Queries; /// public sealed class HideRelationshipsSparseFieldSetCache : ISparseFieldSetCache @@ -15,8 +15,8 @@ public sealed class HideRelationshipsSparseFieldSetCache : ISparseFieldSetCache public HideRelationshipsSparseFieldSetCache(IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor) { - ArgumentGuard.NotNull(constraintProviders); - ArgumentGuard.NotNull(resourceDefinitionAccessor); + ArgumentNullException.ThrowIfNull(constraintProviders); + ArgumentNullException.ThrowIfNull(resourceDefinitionAccessor); _innerCache = new SparseFieldSetCache(constraintProviders, resourceDefinitionAccessor); } @@ -24,18 +24,24 @@ public HideRelationshipsSparseFieldSetCache(IEnumerable public IImmutableSet GetSparseFieldSetForQuery(ResourceType resourceType) { + ArgumentNullException.ThrowIfNull(resourceType); + return _innerCache.GetSparseFieldSetForQuery(resourceType); } /// public IImmutableSet GetIdAttributeSetForRelationshipQuery(ResourceType resourceType) { + ArgumentNullException.ThrowIfNull(resourceType); + return _innerCache.GetIdAttributeSetForRelationshipQuery(resourceType); } /// public IImmutableSet GetSparseFieldSetForSerializer(ResourceType resourceType) { + ArgumentNullException.ThrowIfNull(resourceType); + IImmutableSet fieldSet = _innerCache.GetSparseFieldSetForSerializer(resourceType); return resourceType.ClrType.IsAssignableTo(typeof(IMongoIdentifiable)) ? RemoveRelationships(fieldSet) : fieldSet; diff --git a/src/JsonApiDotNetCore.MongoDb/ReadOnlySet.cs b/src/JsonApiDotNetCore.MongoDb/ReadOnlySet.cs new file mode 100644 index 0000000..9917af9 --- /dev/null +++ b/src/JsonApiDotNetCore.MongoDb/ReadOnlySet.cs @@ -0,0 +1,171 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET8_0 +#pragma warning disable + +// ReadOnlySet was introduced in .NET 9. +// This file was copied from https://github.com/dotnet/runtime/blob/release/9.0/src/libraries/System.Collections/src/System/Collections/Generic/ReadOnlySet.cs +// and made internal to enable usage on lower .NET versions. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Collections.ObjectModel; + +/// Represents a read-only, generic set of values. +/// The type of values in the set. +[DebuggerDisplay("Count = {Count}")] +[ExcludeFromCodeCoverage] +internal class ReadOnlySet : IReadOnlySet, ISet, ICollection +{ + /// The wrapped set. + private readonly ISet _set; + + /// Initializes a new instance of the class that is a wrapper around the specified set. + /// The set to wrap. + public ReadOnlySet(ISet set) + { + ArgumentNullException.ThrowIfNull(set); + _set = set; + } + + /// Gets an empty . + public static ReadOnlySet Empty { get; } = new ReadOnlySet(new HashSet()); + + /// Gets the set that is wrapped by this object. + protected ISet Set => _set; + + /// + public int Count => _set.Count; + + /// + public IEnumerator GetEnumerator() => + _set.Count == 0 ? ((IEnumerable)Array.Empty()).GetEnumerator() : + _set.GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + public bool Contains(T item) => _set.Contains(item); + + /// + public bool IsProperSubsetOf(IEnumerable other) => _set.IsProperSubsetOf(other); + + /// + public bool IsProperSupersetOf(IEnumerable other) => _set.IsProperSupersetOf(other); + + /// + public bool IsSubsetOf(IEnumerable other) => _set.IsSubsetOf(other); + + /// + public bool IsSupersetOf(IEnumerable other) => _set.IsSupersetOf(other); + + /// + public bool Overlaps(IEnumerable other) => _set.Overlaps(other); + + /// + public bool SetEquals(IEnumerable other) => _set.SetEquals(other); + + /// + void ICollection.CopyTo(T[] array, int arrayIndex) => _set.CopyTo(array, arrayIndex); + + /// + void ICollection.CopyTo(Array array, int index) => CollectionHelpers.CopyTo(_set, array, index); + + /// + bool ICollection.IsReadOnly => true; + + /// + bool ICollection.IsSynchronized => false; + + /// + object ICollection.SyncRoot => _set is ICollection c ? c.SyncRoot : this; + + /// + bool ISet.Add(T item) => throw new NotSupportedException(); + + /// + void ISet.ExceptWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.IntersectWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.SymmetricExceptWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ISet.UnionWith(IEnumerable other) => throw new NotSupportedException(); + + /// + void ICollection.Add(T item) => throw new NotSupportedException(); + + /// + void ICollection.Clear() => throw new NotSupportedException(); + + /// + bool ICollection.Remove(T item) => throw new NotSupportedException(); + + private static class CollectionHelpers + { + private static void ValidateCopyToArguments(int sourceCount, Array array, int index) + { + ArgumentNullException.ThrowIfNull(array); + + if (array.Rank != 1) + { + throw new ArgumentException("Only single dimensional arrays are supported for the requested action.", nameof(array)); + } + + if (array.GetLowerBound(0) != 0) + { + throw new ArgumentException("The lower bound of target array must be zero.", nameof(array)); + } + + ArgumentOutOfRangeException.ThrowIfNegative(index); + ArgumentOutOfRangeException.ThrowIfGreaterThan(index, array.Length); + + if (array.Length - index < sourceCount) + { + throw new ArgumentException("Destination array is not long enough to copy all the items in the collection. Check array index and length."); + } + } + + internal static void CopyTo(ICollection collection, Array array, int index) + { + ValidateCopyToArguments(collection.Count, array, index); + + if (collection is ICollection nonGenericCollection) + { + // Easy out if the ICollection implements the non-generic ICollection + nonGenericCollection.CopyTo(array, index); + } + else if (array is T[] items) + { + collection.CopyTo(items, index); + } + else + { + // We can't cast array of value type to object[], so we don't support widening of primitive types here. + if (array is not object?[] objects) + { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection.", nameof(array)); + } + + try + { + foreach (T item in collection) + { + objects[index++] = item; + } + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException("Target array type is not compatible with the type of items in the collection.", nameof(array)); + } + } + } + } +} +#endif diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs index 018b0a4..a92db8c 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoDataAccess.cs @@ -20,8 +20,8 @@ public sealed class MongoDataAccess : IMongoDataAccess public MongoDataAccess(IReadOnlyModel entityModel, IMongoDatabase mongoDatabase) { - ArgumentGuard.NotNull(entityModel); - ArgumentGuard.NotNull(mongoDatabase); + ArgumentNullException.ThrowIfNull(entityModel); + ArgumentNullException.ThrowIfNull(mongoDatabase); EntityModel = entityModel; MongoDatabase = mongoDatabase; diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs index fe8ce6c..c1deeed 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoQueryExpressionValidator.cs @@ -10,9 +10,9 @@ internal sealed class MongoQueryExpressionValidator : QueryExpressionRewriter 0; if (hasIncludes || HasSparseRelationshipSets(layer.Selection)) { @@ -52,7 +52,7 @@ private void ValidateExpression(QueryExpression? expression) public override QueryExpression VisitResourceFieldChain(ResourceFieldChainExpression expression, object? argument) { - if (expression.Fields.Count > 1 || expression.Fields.First() is RelationshipAttribute) + if (expression.Fields.Count > 1 || expression.Fields[0] is RelationshipAttribute) { throw new UnsupportedRelationshipException(); } diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index 579531c..0221b00 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -1,3 +1,4 @@ +using System.Data; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using JetBrains.Annotations; @@ -34,7 +35,7 @@ public class MongoRepository : IResourceRepository _constraintProviders; + private readonly IQueryConstraintProvider[] _constraintProviders; private readonly IResourceDefinitionAccessor _resourceDefinitionAccessor; private readonly IQueryableBuilder _queryableBuilder; @@ -46,19 +47,19 @@ public class MongoRepository : IResourceRepository constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder) { - ArgumentGuard.NotNull(mongoDataAccess); - ArgumentGuard.NotNull(targetedFields); - ArgumentGuard.NotNull(resourceGraph); - ArgumentGuard.NotNull(resourceFactory); - ArgumentGuard.NotNull(constraintProviders); - ArgumentGuard.NotNull(resourceDefinitionAccessor); - ArgumentGuard.NotNull(queryableBuilder); + ArgumentNullException.ThrowIfNull(mongoDataAccess); + ArgumentNullException.ThrowIfNull(targetedFields); + ArgumentNullException.ThrowIfNull(resourceGraph); + ArgumentNullException.ThrowIfNull(resourceFactory); + ArgumentNullException.ThrowIfNull(constraintProviders); + ArgumentNullException.ThrowIfNull(resourceDefinitionAccessor); + ArgumentNullException.ThrowIfNull(queryableBuilder); _mongoDataAccess = mongoDataAccess; _targetedFields = targetedFields; _resourceGraph = resourceGraph; _resourceFactory = resourceFactory; - _constraintProviders = constraintProviders; + _constraintProviders = constraintProviders as IQueryConstraintProvider[] ?? constraintProviders.ToArray(); _resourceDefinitionAccessor = resourceDefinitionAccessor; _queryableBuilder = queryableBuilder; @@ -71,10 +72,11 @@ public MongoRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete /// public virtual async Task> GetAsync(QueryLayer queryLayer, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(queryLayer); + ArgumentNullException.ThrowIfNull(queryLayer); IMongoQueryable query = ApplyQueryLayer(queryLayer); - return await query.ToListAsync(cancellationToken); + List? resources = await query.ToListAsync(cancellationToken); + return resources.AsReadOnly(); } /// @@ -95,7 +97,7 @@ public virtual Task CountAsync(FilterExpression? topFilter, CancellationTok protected virtual IMongoQueryable ApplyQueryLayer(QueryLayer queryLayer) #pragma warning restore AV1130 // Return type in method signature should be an interface to an unchangeable collection { - ArgumentGuard.NotNull(queryLayer); + ArgumentNullException.ThrowIfNull(queryLayer); var queryExpressionValidator = new MongoQueryExpressionValidator(); queryExpressionValidator.Validate(queryLayer); @@ -159,6 +161,8 @@ private void AssertNoRelationshipsInSparseFieldSets() /// public virtual Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(resourceClrType); + var resource = (TResource)_resourceFactory.CreateInstance(resourceClrType); resource.Id = id; @@ -168,8 +172,8 @@ public virtual Task GetForCreateAsync(Type resourceClrType, [Disallow /// public virtual async Task CreateAsync(TResource resourceFromRequest, TResource resourceForDatabase, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(resourceFromRequest); - ArgumentGuard.NotNull(resourceForDatabase); + ArgumentNullException.ThrowIfNull(resourceFromRequest); + ArgumentNullException.ThrowIfNull(resourceForDatabase); AssertNoRelationshipsAreTargeted(); @@ -190,7 +194,7 @@ await SaveChangesAsync( private void AssertNoRelationshipsAreTargeted() { - if (_targetedFields.Relationships.Any()) + if (_targetedFields.Relationships.Count > 0) { throw new UnsupportedRelationshipException(); } @@ -199,7 +203,7 @@ private void AssertNoRelationshipsAreTargeted() /// public virtual async Task GetForUpdateAsync(QueryLayer queryLayer, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(queryLayer); + ArgumentNullException.ThrowIfNull(queryLayer); IReadOnlyCollection resources = await GetAsync(queryLayer, cancellationToken); return resources.FirstOrDefault(); @@ -208,8 +212,8 @@ private void AssertNoRelationshipsAreTargeted() /// public virtual async Task UpdateAsync(TResource resourceFromRequest, TResource resourceFromDatabase, CancellationToken cancellationToken) { - ArgumentGuard.NotNull(resourceFromRequest); - ArgumentGuard.NotNull(resourceFromDatabase); + ArgumentNullException.ThrowIfNull(resourceFromRequest); + ArgumentNullException.ThrowIfNull(resourceFromDatabase); AssertNoRelationshipsAreTargeted(); @@ -248,12 +252,12 @@ public virtual async Task DeleteAsync(TResource? resourceFromDatabase, [Disallow if (!result.IsAcknowledged) { throw new DataStoreUpdateException( - new Exception($"Failed to delete document with id '{id}', because the operation was not acknowledged by MongoDB.")); + new DataException($"Failed to delete document with id '{id}', because the operation was not acknowledged by MongoDB.")); } if (result.DeletedCount == 0) { - throw new DataStoreUpdateException(new Exception($"Failed to delete document with id '{id}', because it does not exist.")); + throw new DataStoreUpdateException(new DataException($"Failed to delete document with id '{id}', because it does not exist.")); } await _resourceDefinitionAccessor.OnWriteSucceededAsync(placeholderResource, WriteOperationKind.DeleteResource, cancellationToken); @@ -280,6 +284,8 @@ public virtual Task RemoveFromToManyRelationshipAsync(TResource leftResource, IS protected virtual async Task SaveChangesAsync(Func asyncSaveAction, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(asyncSaveAction); + _ = await SaveChangesAsync(async () => { await asyncSaveAction(); @@ -289,6 +295,8 @@ protected virtual async Task SaveChangesAsync(Func asyncSaveAction, Cancel protected virtual async Task SaveChangesAsync(Func> asyncSaveAction, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(asyncSaveAction); + try { return await asyncSaveAction(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsTestCollection.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsCollectionFixture.cs similarity index 81% rename from test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsTestCollection.cs rename to test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsCollectionFixture.cs index acd9a96..dd1be80 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsTestCollection.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/AtomicOperationsCollectionFixture.cs @@ -3,7 +3,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [CollectionDefinition("AtomicOperationsFixture")] -public sealed class AtomicOperationsTestCollection : ICollectionFixture +public sealed class AtomicOperationsCollectionFixture : ICollectionFixture { // Starting MongoDB in Single Node Replica Set mode is required to enable transactions. // Starting in this mode requires about 10 seconds, which is normally repeated for each test class. diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs index 6f60018..e9ffc53 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/BaseForAtomicOperationsTestsThatChangeOptions.cs @@ -16,12 +16,23 @@ protected BaseForAtomicOperationsTestsThatChangeOptions(AtomicOperationsFixture public void Dispose() { - _optionsScope.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + +#pragma warning disable CA1063 // Implement IDisposable Correctly + private void Dispose(bool disposing) +#pragma warning restore CA1063 // Implement IDisposable Correctly + { + if (disposing) + { + _optionsScope.Dispose(); + } } private sealed class JsonApiOptionsScope : IDisposable { - private static readonly List PropertyCache = typeof(JsonApiOptions).GetProperties().Where(IsAccessibleProperty).ToList(); + private static readonly PropertyInfo[] PropertyCache = typeof(JsonApiOptions).GetProperties().Where(IsAccessibleProperty).ToArray(); private readonly JsonApiOptions _options; private readonly JsonApiOptions _backupValues; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs index 0172c7a..8d65046 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceTests.cs @@ -17,8 +17,8 @@ public sealed class AtomicCreateResourceTests(AtomicOperationsFixture fixture) public async Task Can_create_resource() { // Arrange - string newArtistName = _fakers.Performer.Generate().ArtistName!; - DateTimeOffset newBornAt = _fakers.Performer.Generate().BornAt; + string newArtistName = _fakers.Performer.GenerateOne().ArtistName!; + DateTimeOffset newBornAt = _fakers.Performer.GenerateOne().BornAt; var requestBody = new { @@ -48,17 +48,17 @@ public async Task Can_create_resource() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(1); + responseDocument.Results.Should().HaveCount(1); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("performers"); - resource.Attributes.ShouldContainKey("artistName").With(value => value.Should().Be(newArtistName)); - resource.Attributes.ShouldContainKey("bornAt").With(value => value.Should().Be(newBornAt)); + resource.Attributes.Should().ContainKey("artistName").WhoseValue.Should().Be(newArtistName); + resource.Attributes.Should().ContainKey("bornAt").WhoseValue.Should().Be(newBornAt); resource.Relationships.Should().BeNull(); }); - string newPerformerId = responseDocument.Results[0].Data.SingleValue!.Id.ShouldNotBeNull(); + string newPerformerId = responseDocument.Results[0].Data.SingleValue!.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -75,7 +75,7 @@ public async Task Can_create_resources() // Arrange const int elementCount = 5; - List newTracks = _fakers.MusicTrack.Generate(elementCount); + List newTracks = _fakers.MusicTrack.GenerateList(elementCount); var operationElements = new List(elementCount); @@ -111,33 +111,30 @@ public async Task Can_create_resources() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(elementCount); + responseDocument.Results.Should().HaveCount(elementCount); for (int index = 0; index < elementCount; index++) { - responseDocument.Results[index].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[index].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { - resource.ShouldNotBeNull(); + resource.Should().NotBeNull(); resource.Type.Should().Be("musicTracks"); - resource.Attributes.ShouldContainKey("title").With(value => value.Should().Be(newTracks[index].Title)); - - resource.Attributes.ShouldContainKey("lengthInSeconds") - .With(value => value.As().Should().BeApproximately(newTracks[index].LengthInSeconds)); - - resource.Attributes.ShouldContainKey("genre").With(value => value.Should().Be(newTracks[index].Genre)); - resource.Attributes.ShouldContainKey("releasedAt").With(value => value.Should().Be(newTracks[index].ReleasedAt)); + resource.Attributes.Should().ContainKey("title").WhoseValue.Should().Be(newTracks[index].Title); + resource.Attributes.Should().ContainKey("lengthInSeconds").WhoseValue.As().Should().BeApproximately(newTracks[index].LengthInSeconds); + resource.Attributes.Should().ContainKey("genre").WhoseValue.Should().Be(newTracks[index].Genre); + resource.Attributes.Should().ContainKey("releasedAt").WhoseValue.Should().Be(newTracks[index].ReleasedAt); resource.Relationships.Should().BeNull(); }); } - string[] newTrackIds = responseDocument.Results.Select(result => result.Data.SingleValue!.Id.ShouldNotBeNull()).ToArray(); + string[] newTrackIds = responseDocument.Results.Select(result => result.Data.SingleValue!.Id.Should().NotBeNull().And.Subject).ToArray(); await _testContext.RunOnDatabaseAsync(async dbContext => { List tracksInDatabase = await dbContext.MusicTracks.ToListWhereAsync(musicTrack => newTrackIds.Contains(musicTrack.Id)); - tracksInDatabase.ShouldHaveCount(elementCount); + tracksInDatabase.Should().HaveCount(elementCount); for (int index = 0; index < elementCount; index++) { @@ -184,17 +181,17 @@ public async Task Can_create_resource_without_attributes_or_relationships() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(1); + responseDocument.Results.Should().HaveCount(1); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("performers"); - resource.Attributes.ShouldContainKey("artistName").With(value => value.Should().BeNull()); - resource.Attributes.ShouldContainKey("bornAt").With(value => value.Should().Be(default(DateTimeOffset))); + resource.Attributes.Should().ContainKey("artistName").WhoseValue.Should().BeNull(); + resource.Attributes.Should().ContainKey("bornAt").WhoseValue.Should().Be(default(DateTimeOffset)); resource.Relationships.Should().BeNull(); }); - string newPerformerId = responseDocument.Results[0].Data.SingleValue!.Id.ShouldNotBeNull(); + string newPerformerId = responseDocument.Results[0].Data.SingleValue!.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -209,7 +206,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_create_resource_with_client_generated_ID() { // Arrange - MusicTrack newTrack = _fakers.MusicTrack.Generate(); + MusicTrack newTrack = _fakers.MusicTrack.GenerateOne(); newTrack.Id = ObjectId.GenerateNewId().ToString(); var requestBody = new @@ -240,14 +237,14 @@ public async Task Cannot_create_resource_with_client_generated_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Forbidden); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.Forbidden); error.Title.Should().Be("Failed to deserialize request body: The use of client-generated IDs is disabled."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]/data/id"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + error.Meta.Should().HaveRequestBody(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs index 7fdf996..6014612 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithClientGeneratedIdTests.cs @@ -27,7 +27,7 @@ public AtomicCreateResourceWithClientGeneratedIdTests(AtomicOperationsFixture fi public async Task Can_create_resource_with_client_generated_string_ID_having_side_effects() { // Arrange - TextLanguage newLanguage = _fakers.TextLanguage.Generate(); + TextLanguage newLanguage = _fakers.TextLanguage.GenerateOne(); newLanguage.Id = "free-format-client-generated-id"; var requestBody = new @@ -60,12 +60,12 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_sid string isoCode = $"{newLanguage.IsoCode}{ImplicitlyChangingTextLanguageDefinition.Suffix}"; - responseDocument.Results.ShouldHaveCount(1); + responseDocument.Results.Should().HaveCount(1); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("textLanguages"); - resource.Attributes.ShouldContainKey("isoCode").With(value => value.Should().Be(isoCode)); + resource.Attributes.Should().ContainKey("isoCode").WhoseValue.Should().Be(isoCode); resource.Attributes.Should().NotContainKey("isRightToLeft"); resource.Relationships.Should().BeNull(); }); @@ -82,7 +82,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_create_resource_with_client_generated_string_ID_having_no_side_effects() { // Arrange - Playlist newPlaylist = _fakers.Playlist.Generate(); + Playlist newPlaylist = _fakers.Playlist.GenerateOne(); newPlaylist.Id = "free-format-client-generated-id"; var requestBody = new @@ -127,10 +127,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_create_resource_for_existing_client_generated_ID() { // Arrange - TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); + TextLanguage existingLanguage = _fakers.TextLanguage.GenerateOne(); existingLanguage.Id = "existing-free-format-client-generated-id"; - string newIsoCode = _fakers.TextLanguage.Generate().IsoCode!; + string newIsoCode = _fakers.TextLanguage.GenerateOne().IsoCode!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -166,13 +166,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Conflict); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.Conflict); error.Title.Should().Be("Another resource with the specified ID already exists."); error.Detail.Should().Be($"Another resource of type 'textLanguages' with ID '{existingLanguage.StringId}' already exists."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); error.Meta.Should().NotContainKey("requestBody"); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs index 70b5468..4f5f561 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToManyRelationshipTests.cs @@ -16,9 +16,9 @@ public sealed class AtomicCreateResourceWithToManyRelationshipTests(AtomicOperat public async Task Cannot_create_ToMany_relationship() { // Arrange - Performer existingPerformer = _fakers.Performer.Generate(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); - string newTitle = _fakers.MusicTrack.Generate().Title; + string newTitle = _fakers.MusicTrack.GenerateOne().Title; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -67,13 +67,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs index 51fd385..e449068 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Creating/AtomicCreateResourceWithToOneRelationshipTests.cs @@ -16,9 +16,9 @@ public sealed class AtomicCreateResourceWithToOneRelationshipTests(AtomicOperati public async Task Cannot_create_ToOne_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); - string newLyricText = _fakers.Lyric.Generate().Text; + string newLyricText = _fakers.Lyric.GenerateOne().Text; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -64,13 +64,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs index cd7accf..1ea1ac4 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Deleting/AtomicDeleteResourceTests.cs @@ -16,7 +16,7 @@ public sealed class AtomicDeleteResourceTests(AtomicOperationsFixture fixture) public async Task Can_delete_existing_resource() { // Arrange - Performer existingPerformer = _fakers.Performer.Generate(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -64,7 +64,7 @@ public async Task Can_delete_existing_resources() // Arrange const int elementCount = 5; - List existingTracks = _fakers.MusicTrack.Generate(elementCount); + List existingTracks = _fakers.MusicTrack.GenerateList(elementCount); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -141,13 +141,13 @@ public async Task Cannot_delete_resource_for_unknown_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); error.Title.Should().Be("The requested resource does not exist."); error.Detail.Should().Be($"Resource of type 'performers' with ID '{performerId}' does not exist."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); error.Meta.Should().NotContainKey("requestBody"); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs index 54680ba..61459ed 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/LocalIds/AtomicLocalIdTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicLocalIdTests(AtomicOperationsFixture fixture) public async Task Can_update_resource_using_local_ID() { // Arrange - string newTrackTitle = _fakers.MusicTrack.Generate().Title; - string newTrackGenre = _fakers.MusicTrack.Generate().Genre!; + string newTrackTitle = _fakers.MusicTrack.GenerateOne().Title; + string newTrackGenre = _fakers.MusicTrack.GenerateOne().Genre!; const string trackLocalId = "track-1"; @@ -62,19 +62,19 @@ public async Task Can_update_resource_using_local_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(2); + responseDocument.Results.Should().HaveCount(2); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("musicTracks"); resource.Lid.Should().BeNull(); - resource.Attributes.ShouldContainKey("title").With(value => value.Should().Be(newTrackTitle)); - resource.Attributes.ShouldContainKey("genre").With(value => value.Should().BeNull()); + resource.Attributes.Should().ContainKey("title").WhoseValue.Should().Be(newTrackTitle); + resource.Attributes.Should().ContainKey("genre").WhoseValue.Should().BeNull(); }); responseDocument.Results[1].Data.Value.Should().BeNull(); - string newTrackId = responseDocument.Results[0].Data.SingleValue!.Id.ShouldNotBeNull(); + string newTrackId = responseDocument.Results[0].Data.SingleValue!.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -89,7 +89,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_delete_resource_using_local_ID() { // Arrange - string newTrackTitle = _fakers.MusicTrack.Generate().Title; + string newTrackTitle = _fakers.MusicTrack.GenerateOne().Title; const string trackLocalId = "track-1"; @@ -130,18 +130,18 @@ public async Task Can_delete_resource_using_local_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(2); + responseDocument.Results.Should().HaveCount(2); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("musicTracks"); resource.Lid.Should().BeNull(); - resource.Attributes.ShouldContainKey("title").With(value => value.Should().Be(newTrackTitle)); + resource.Attributes.Should().ContainKey("title").WhoseValue.Should().Be(newTrackTitle); }); responseDocument.Results[1].Data.Value.Should().BeNull(); - string newTrackId = responseDocument.Results[0].Data.SingleValue!.Id.ShouldNotBeNull(); + string newTrackId = responseDocument.Results[0].Data.SingleValue!.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs index a701256..988b697 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Meta/AtomicResourceMetaTests.cs @@ -29,8 +29,8 @@ public async Task Returns_resource_meta_in_create_resource_with_side_effects() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - string newTitle1 = _fakers.MusicTrack.Generate().Title; - string newTitle2 = _fakers.MusicTrack.Generate().Title; + string newTitle1 = _fakers.MusicTrack.GenerateOne().Title; + string newTitle2 = _fakers.MusicTrack.GenerateOne().Title; var requestBody = new { @@ -73,24 +73,24 @@ public async Task Returns_resource_meta_in_create_resource_with_side_effects() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(2); + responseDocument.Results.Should().HaveCount(2); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { - resource.Meta.ShouldHaveCount(1); + resource.Meta.Should().HaveCount(1); - resource.Meta.ShouldContainKey("copyright").With(value => + resource.Meta.Should().ContainKey("copyright").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; element.GetString().Should().Be("(C) 2018. All rights reserved."); }); }); - responseDocument.Results[1].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[1].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { - resource.Meta.ShouldHaveCount(1); + resource.Meta.Should().HaveCount(1); - resource.Meta.ShouldContainKey("copyright").With(value => + resource.Meta.Should().ContainKey("copyright").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; element.GetString().Should().Be("(C) 1994. All rights reserved."); @@ -110,7 +110,7 @@ public async Task Returns_resource_meta_in_update_resource_with_side_effects() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); + TextLanguage existingLanguage = _fakers.TextLanguage.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -145,13 +145,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(1); + responseDocument.Results.Should().HaveCount(1); - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { - resource.Meta.ShouldHaveCount(1); + resource.Meta.Should().HaveCount(1); - resource.Meta.ShouldContainKey("notice").With(value => + resource.Meta.Should().ContainKey("notice").WhoseValue.With(value => { JsonElement element = value.Should().BeOfType().Subject; element.GetString().Should().Be(TextLanguageMetaDefinition.NoticeText); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs index ba4bf6a..d405a69 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Mixed/MaximumOperationsPerRequestTests.cs @@ -8,7 +8,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Mixed; [Collection("AtomicOperationsFixture")] -public sealed class MaximumOperationsPerRequestTests(AtomicOperationsFixture fixture) : BaseForAtomicOperationsTestsThatChangeOptions(fixture) +public sealed class MaximumOperationsPerRequestTests(AtomicOperationsFixture fixture) + : BaseForAtomicOperationsTestsThatChangeOptions(fixture) { private readonly IntegrationTestContext _testContext = fixture.TestContext; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs index d8ec189..1ba04d2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsController.cs @@ -9,5 +9,5 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; public sealed class OperationsController( IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, - ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, - request, targetedFields, operationFilter); + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) + : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs index aa75333..5d443a6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class OperationsDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class OperationsDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim Playlists => Set(); public MongoDbSetShim MusicTracks => Set(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs index 51f3ba8..6a10744 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/OperationsFakers.cs @@ -9,7 +9,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations; internal sealed class OperationsFakers { - private static readonly Lazy> LazyLanguageIsoCodes = new(() => CultureInfo + private static readonly Lazy LazyLanguageIsoCodes = new(() => CultureInfo .GetCultures(CultureTypes.NeutralCultures) .Where(culture => !string.IsNullOrEmpty(culture.Name)) .Select(culture => culture.Name) @@ -33,7 +33,7 @@ internal sealed class OperationsFakers private readonly Lazy> _lazyTextLanguageFaker = new(() => new Faker() .MakeDeterministic() - .RuleFor(textLanguage => textLanguage.IsoCode, faker => faker.PickRandom(LazyLanguageIsoCodes.Value))); + .RuleFor(textLanguage => textLanguage.IsoCode, faker => faker.PickRandom(LazyLanguageIsoCodes.Value))); private readonly Lazy> _lazyPerformerFaker = new(() => new Faker() .MakeDeterministic() diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs index 0a2cc3a..08bd99a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicRollbackTests.cs @@ -23,8 +23,8 @@ public AtomicRollbackTests(AtomicOperationsFixture fixture) public async Task Can_rollback_created_resource_on_error() { // Arrange - string newArtistName = _fakers.Performer.Generate().ArtistName!; - DateTimeOffset newBornAt = _fakers.Performer.Generate().BornAt; + string newArtistName = _fakers.Performer.GenerateOne().ArtistName!; + DateTimeOffset newBornAt = _fakers.Performer.GenerateOne().BornAt; await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.ClearTableAsync()); @@ -67,13 +67,13 @@ public async Task Can_rollback_created_resource_on_error() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); error.Title.Should().Be("The requested resource does not exist."); error.Detail.Should().Be($"Resource of type 'performers' with ID '{unknownPerformerId}' does not exist."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[1]"); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -87,9 +87,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_rollback_updated_resource_on_error() { // Arrange - Performer existingPerformer = _fakers.Performer.Generate(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); - string newArtistName = _fakers.Performer.Generate().ArtistName!; + string newArtistName = _fakers.Performer.GenerateOne().ArtistName!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -136,13 +136,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); error.Title.Should().Be("The requested resource does not exist."); error.Detail.Should().Be($"Resource of type 'performers' with ID '{unknownPerformerId}' does not exist."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[1]"); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -157,7 +157,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_rollback_deleted_resource_on_error() { // Arrange - Performer existingPerformer = _fakers.Performer.Generate(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -201,13 +201,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); error.Title.Should().Be("The requested resource does not exist."); error.Detail.Should().Be($"Resource of type 'performers' with ID '{unknownPerformerId}' does not exist."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[1]"); await _testContext.RunOnDatabaseAsync(async dbContext => diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs index 5475fcb..b19501b 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/AtomicTransactionConsistencyTests.cs @@ -2,7 +2,6 @@ using FluentAssertions; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.Extensions.DependencyInjection.Extensions; using TestBuildingBlocks; using Xunit; @@ -23,8 +22,6 @@ public AtomicTransactionConsistencyTests(IntegrationTestContext { - services.TryAddSingleton(); - services.AddResourceRepository(); services.AddResourceRepository(); services.AddResourceRepository(); @@ -61,13 +58,13 @@ public async Task Cannot_use_non_transactional_repository() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Unsupported resource type in atomic:operations request."); error.Detail.Should().Be("Operations on resources of type 'performers' cannot be used because transaction support is unavailable."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } @@ -75,7 +72,7 @@ public async Task Cannot_use_non_transactional_repository() public async Task Cannot_use_transactional_repository_without_active_transaction() { // Arrange - string newTrackTitle = _fakers.MusicTrack.Generate().Title; + string newTrackTitle = _fakers.MusicTrack.GenerateOne().Title; var requestBody = new { @@ -104,13 +101,13 @@ public async Task Cannot_use_transactional_repository_without_active_transaction // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Unsupported combination of resource types in atomic:operations request."); error.Detail.Should().Be("All operations need to participate in a single shared transaction, which is not the case for this request."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } @@ -118,7 +115,7 @@ public async Task Cannot_use_transactional_repository_without_active_transaction public async Task Cannot_use_distributed_transaction() { // Arrange - string newLyricText = _fakers.Lyric.Generate().Text; + string newLyricText = _fakers.Lyric.GenerateOne().Text; var requestBody = new { @@ -147,13 +144,13 @@ public async Task Cannot_use_distributed_transaction() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.UnprocessableEntity); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.UnprocessableEntity); error.Title.Should().Be("Unsupported combination of resource types in atomic:operations request."); error.Detail.Should().Be("All operations need to participate in a single shared transaction, which is not the case for this request."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs index d8ad7e0..90d71d0 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Transactions/LyricRepository.cs @@ -12,6 +12,7 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.AtomicOperations.Transa [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] public sealed class LyricRepository : MongoRepository, IAsyncDisposable { + private readonly MongoDataAccess _otherDataAccess; private readonly IOperationsTransaction _transaction; public override string TransactionId => _transaction.TransactionId; @@ -20,14 +21,15 @@ public LyricRepository(IMongoDataAccess mongoDataAccess, ITargetedFields targete IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder) : base(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, queryableBuilder) { - IMongoDataAccess otherDataAccess = new MongoDataAccess(mongoDataAccess.EntityModel, mongoDataAccess.MongoDatabase); + _otherDataAccess = new MongoDataAccess(mongoDataAccess.EntityModel, mongoDataAccess.MongoDatabase); - var factory = new MongoTransactionFactory(otherDataAccess); + var factory = new MongoTransactionFactory(_otherDataAccess); _transaction = factory.BeginTransactionAsync(CancellationToken.None).Result; } public async ValueTask DisposeAsync() { await _transaction.DisposeAsync(); + await _otherDataAccess.DisposeAsync(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs index 50382cb..e461a70 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicAddToToManyRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicAddToToManyRelationshipTests(AtomicOperationsFixture f public async Task Cannot_add_to_OneToMany_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - Performer existingPerformer = _fakers.Performer.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -59,13 +59,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } @@ -73,8 +73,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_add_to_ManyToMany_relationship() { // Arrange - Playlist existingPlaylist = _fakers.Playlist.Generate(); - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + Playlist existingPlaylist = _fakers.Playlist.GenerateOne(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -116,13 +116,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs index 8ecb491..e68e8fd 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicRemoveFromToManyRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicRemoveFromToManyRelationshipTests(AtomicOperationsFixt public async Task Cannot_remove_from_OneToMany_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - existingTrack.Performers = _fakers.Performer.Generate(1); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); + existingTrack.Performers = _fakers.Performer.GenerateList(1); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -59,13 +59,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } @@ -73,8 +73,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_remove_from_ManyToMany_relationship() { // Arrange - Playlist existingPlaylist = _fakers.Playlist.Generate(); - existingPlaylist.Tracks = _fakers.MusicTrack.Generate(1); + Playlist existingPlaylist = _fakers.Playlist.GenerateOne(); + existingPlaylist.Tracks = _fakers.MusicTrack.GenerateList(1); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -116,13 +116,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs index 09f2979..e71ef7b 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicReplaceToManyRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture public async Task Cannot_replace_OneToMany_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - Performer existingPerformer = _fakers.Performer.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -59,13 +59,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } @@ -73,8 +73,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_replace_ManyToMany_relationship() { // Arrange - Playlist existingPlaylist = _fakers.Playlist.Generate(); - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + Playlist existingPlaylist = _fakers.Playlist.GenerateOne(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -116,13 +116,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs index 2541703..e51e98d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Relationships/AtomicUpdateToOneRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture f public async Task Cannot_create_ManyToOne_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - RecordCompany existingCompany = _fakers.RecordCompany.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); + RecordCompany existingCompany = _fakers.RecordCompany.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -56,13 +56,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs index 329d74d..8ebe878 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicReplaceToManyRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicReplaceToManyRelationshipTests(AtomicOperationsFixture public async Task Cannot_replace_ToMany_relationship() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); - Performer existingPerformer = _fakers.Performer.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); + Performer existingPerformer = _fakers.Performer.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -64,13 +64,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs index 9b88500..d438d34 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateResourceTests.cs @@ -18,8 +18,8 @@ public async Task Can_update_resources() // Arrange const int elementCount = 5; - List existingTracks = _fakers.MusicTrack.Generate(elementCount); - string[] newTrackTitles = _fakers.MusicTrack.Generate(elementCount).Select(musicTrack => musicTrack.Title).ToArray(); + List existingTracks = _fakers.MusicTrack.GenerateList(elementCount); + string[] newTrackTitles = _fakers.MusicTrack.GenerateList(elementCount).Select(musicTrack => musicTrack.Title).ToArray(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -66,7 +66,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => { List tracksInDatabase = await dbContext.MusicTracks.ToListAsync(); - tracksInDatabase.ShouldHaveCount(elementCount); + tracksInDatabase.Should().HaveCount(elementCount); for (int index = 0; index < elementCount; index++) { @@ -82,7 +82,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_update_resource_without_attributes_or_relationships() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -135,9 +135,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_partially_update_resource_without_side_effects() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); - string newGenre = _fakers.MusicTrack.Generate().Genre!; + string newGenre = _fakers.MusicTrack.GenerateOne().Genre!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -190,12 +190,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_completely_update_resource_without_side_effects() { // Arrange - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); - string newTitle = _fakers.MusicTrack.Generate().Title; - decimal? newLengthInSeconds = _fakers.MusicTrack.Generate().LengthInSeconds; - string newGenre = _fakers.MusicTrack.Generate().Genre!; - DateTimeOffset newReleasedAt = _fakers.MusicTrack.Generate().ReleasedAt; + string newTitle = _fakers.MusicTrack.GenerateOne().Title; + decimal? newLengthInSeconds = _fakers.MusicTrack.GenerateOne().LengthInSeconds; + string newGenre = _fakers.MusicTrack.GenerateOne().Genre!; + DateTimeOffset newReleasedAt = _fakers.MusicTrack.GenerateOne().ReleasedAt; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -251,8 +251,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_update_resource_with_side_effects() { // Arrange - TextLanguage existingLanguage = _fakers.TextLanguage.Generate(); - string newIsoCode = _fakers.TextLanguage.Generate().IsoCode!; + TextLanguage existingLanguage = _fakers.TextLanguage.GenerateOne(); + string newIsoCode = _fakers.TextLanguage.GenerateOne().IsoCode!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -288,14 +288,14 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Results.ShouldHaveCount(1); + responseDocument.Results.Should().HaveCount(1); string isoCode = $"{newIsoCode}{ImplicitlyChangingTextLanguageDefinition.Suffix}"; - responseDocument.Results[0].Data.SingleValue.ShouldNotBeNull().With(resource => + responseDocument.Results[0].Data.SingleValue.RefShould().NotBeNull().And.Subject.With(resource => { resource.Type.Should().Be("textLanguages"); - resource.Attributes.ShouldContainKey("isoCode").With(value => value.Should().Be(isoCode)); + resource.Attributes.Should().ContainKey("isoCode").WhoseValue.Should().Be(isoCode); resource.Attributes.Should().NotContainKey("isRightToLeft"); resource.Relationships.Should().BeNull(); }); @@ -343,13 +343,13 @@ public async Task Cannot_update_resource_for_unknown_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); error.Title.Should().Be("The requested resource does not exist."); error.Detail.Should().Be($"Resource of type 'performers' with ID '{performerId}' does not exist."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); error.Meta.Should().NotContainKey("requestBody"); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs index 75734f4..21967d5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/AtomicOperations/Updating/Resources/AtomicUpdateToOneRelationshipTests.cs @@ -16,8 +16,8 @@ public sealed class AtomicUpdateToOneRelationshipTests(AtomicOperationsFixture f public async Task Cannot_create_ToOne_relationship() { // Arrange - Lyric existingLyric = _fakers.Lyric.Generate(); - MusicTrack existingTrack = _fakers.MusicTrack.Generate(); + Lyric existingLyric = _fakers.Lyric.GenerateOne(); + MusicTrack existingTrack = _fakers.MusicTrack.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -61,13 +61,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Relationships are not supported when using MongoDB."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/atomic:operations[0]"); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs index 3ee880d..d059689 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/HitCountingResourceDefinition.cs @@ -11,14 +11,21 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests; /// Tracks invocations on callback methods. This is used solely in our tests, so we can assert which /// calls were made, and in which order. /// -public abstract class HitCountingResourceDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) - : JsonApiResourceDefinition(resourceGraph) +public abstract class HitCountingResourceDefinition : JsonApiResourceDefinition where TResource : class, IIdentifiable { - private readonly ResourceDefinitionHitCounter _hitCounter = hitCounter; + private readonly ResourceDefinitionHitCounter _hitCounter; protected virtual ResourceDefinitionExtensibilityPoints ExtensibilityPointsToTrack => ResourceDefinitionExtensibilityPoints.All; + protected HitCountingResourceDefinition(IResourceGraph resourceGraph, ResourceDefinitionHitCounter hitCounter) + : base(resourceGraph) + { + ArgumentNullException.ThrowIfNull(hitCounter); + + _hitCounter = hitCounter; + } + public override IImmutableSet OnApplyIncludes(IImmutableSet existingIncludes) { if (ExtensibilityPointsToTrack.HasFlag(ResourceDefinitionExtensibilityPoints.OnApplyIncludes)) diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs index 8aa82f8..ec003be 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/MetaDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.Meta; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class MetaDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class MetaDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim SupportTickets => Set(); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/ResourceMetaTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/ResourceMetaTests.cs index d92419c..f8eedf2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/ResourceMetaTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/ResourceMetaTests.cs @@ -3,7 +3,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using TestBuildingBlocks; using Xunit; @@ -24,8 +23,9 @@ public ResourceMetaTests(IntegrationTestContext testContext.ConfigureServices(services => { - services.TryAddSingleton(); services.AddResourceDefinition(); + + services.AddSingleton(); }); var hitCounter = _testContext.Factory.Services.GetRequiredService(); @@ -38,7 +38,7 @@ public async Task Returns_resource_meta_from_ResourceDefinition() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List tickets = _fakers.SupportTicket.Generate(3); + List tickets = _fakers.SupportTicket.GenerateList(3); tickets[0].Description = $"Critical: {tickets[0].Description}"; tickets[2].Description = $"Critical: {tickets[2].Description}"; @@ -57,10 +57,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(3); - responseDocument.Data.ManyValue[0].Meta.ShouldContainKey("hasHighPriority"); + responseDocument.Data.ManyValue.Should().HaveCount(3); + responseDocument.Data.ManyValue[0].Meta.Should().ContainKey("hasHighPriority"); responseDocument.Data.ManyValue[1].Meta.Should().BeNull(); - responseDocument.Data.ManyValue[2].Meta.ShouldContainKey("hasHighPriority"); + responseDocument.Data.ManyValue[2].Meta.Should().ContainKey("hasHighPriority"); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs index 13bb952..c85635d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/Meta/TopLevelCountTests.cs @@ -4,7 +4,6 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using TestBuildingBlocks; using Xunit; @@ -23,7 +22,7 @@ public TopLevelCountTests(IntegrationTestContext testContext.UseController(); - testContext.ConfigureServices(services => services.TryAddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>))); + testContext.ConfigureServices(services => services.AddScoped(typeof(IResourceChangeTracker<>), typeof(NeverSameResourceChangeTracker<>))); var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); options.IncludeTotalResourceCount = true; @@ -33,7 +32,7 @@ public TopLevelCountTests(IntegrationTestContext public async Task Renders_resource_count_for_collection() { // Arrange - SupportTicket ticket = _fakers.SupportTicket.Generate(); + SupportTicket ticket = _fakers.SupportTicket.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -50,8 +49,6 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Meta.ShouldNotBeNull(); - responseDocument.Meta.Should().ContainTotal(1); } @@ -69,8 +66,6 @@ public async Task Renders_resource_count_for_empty_collection() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Meta.ShouldNotBeNull(); - responseDocument.Meta.Should().ContainTotal(0); } @@ -78,7 +73,7 @@ public async Task Renders_resource_count_for_empty_collection() public async Task Hides_resource_count_in_create_resource_response() { // Arrange - string newDescription = _fakers.SupportTicket.Generate().Description; + string newDescription = _fakers.SupportTicket.GenerateOne().Description; var requestBody = new { @@ -107,9 +102,9 @@ public async Task Hides_resource_count_in_create_resource_response() public async Task Hides_resource_count_in_update_resource_response() { // Arrange - SupportTicket existingTicket = _fakers.SupportTicket.Generate(); + SupportTicket existingTicket = _fakers.SupportTicket.GenerateOne(); - string newDescription = _fakers.SupportTicket.Generate().Description; + string newDescription = _fakers.SupportTicket.GenerateOne().Description; await _testContext.RunOnDatabaseAsync(async dbContext => { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs index a74e6ee..7027b2a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDataTypeTests.cs @@ -1,11 +1,14 @@ using System.Globalization; using System.Net; using System.Reflection; +using System.Text.Json.Serialization; using System.Web; using FluentAssertions; using FluentAssertions.Extensions; using Humanizer; +using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; +using Microsoft.Extensions.DependencyInjection; using TestBuildingBlocks; using Xunit; @@ -22,6 +25,13 @@ public FilterDataTypeTests(IntegrationTestContext(); + + var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); + + if (!options.SerializerOptions.Converters.Any(converter => converter is JsonStringEnumConverter)) + { + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + } } [Theory] @@ -62,8 +72,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey(attributeName).With(value => value.Should().Be(value)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey(attributeName).WhoseValue.Should().Be(propertyValue); } [Fact] @@ -90,8 +100,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDecimal").With(value => value.Should().Be(resource.SomeDecimal)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDecimal").WhoseValue.Should().Be(resource.SomeDecimal); } [Fact] @@ -118,8 +128,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someGuid").With(value => value.Should().Be(resource.SomeGuid)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someGuid").WhoseValue.Should().Be(resource.SomeGuid); } [Fact] @@ -146,10 +156,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDateTimeInLocalZone") - .With(value => value.Should().Be(resource.SomeDateTimeInLocalZone)); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateTimeInLocalZone").WhoseValue.Should().Be(resource.SomeDateTimeInLocalZone); } [Fact] @@ -176,10 +185,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDateTimeInUtcZone") - .With(value => value.Should().Be(resource.SomeDateTimeInUtcZone)); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateTimeInUtcZone").WhoseValue.Should().Be(resource.SomeDateTimeInUtcZone); } [Fact] @@ -206,8 +214,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDateTimeOffset").With(value => value.Should().Be(resource.SomeDateTimeOffset)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateTimeOffset").WhoseValue.Should().Be(resource.SomeDateTimeOffset); } [Fact] @@ -234,8 +242,64 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someTimeSpan").With(value => value.Should().Be(resource.SomeTimeSpan)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someTimeSpan").WhoseValue.Should().Be(resource.SomeTimeSpan); + } + + [Fact] + public async Task Can_filter_equality_on_type_DateOnly() + { + // Arrange + var resource = new FilterableResource + { + SomeDateOnly = DateOnly.FromDateTime(27.January(2003)) + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.FilterableResources.AddRange(resource, new FilterableResource()); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/filterableResources?filter=equals(someDateOnly,'{resource.SomeDateOnly:O}')"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateOnly").WhoseValue.Should().Be(resource.SomeDateOnly); + } + + [Fact] + public async Task Can_filter_equality_on_type_TimeOnly() + { + // Arrange + var resource = new FilterableResource + { + SomeTimeOnly = new TimeOnly(23, 59, 59, 999) + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.FilterableResources.AddRange(resource, new FilterableResource()); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/filterableResources?filter=equals(someTimeOnly,'{resource.SomeTimeOnly:O}')"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someTimeOnly").WhoseValue.Should().Be(resource.SomeTimeOnly); } [Fact] @@ -254,7 +318,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await dbContext.SaveChangesAsync(); }); - const string route = "/filterableResources?filter=equals(someInt32,'ABC')"; + var parameterValue = new MarkedText("equals(someInt32,^'ABC')", '^'); + string route = $"/filterableResources?filter={parameterValue.Text}"; // Act (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); @@ -262,13 +327,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("The specified filter is invalid."); - error.Detail.Should().StartWith("Failed to convert 'ABC' of type 'String' to type 'Int32'."); - error.Source.ShouldNotBeNull(); + error.Detail.Should().Be($"Failed to convert 'ABC' of type 'String' to type 'Int32'. {parameterValue}"); + error.Source.Should().NotBeNull(); error.Source.Parameter.Should().Be("filter"); } @@ -283,6 +348,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => [InlineData(nameof(FilterableResource.SomeNullableDateTime))] [InlineData(nameof(FilterableResource.SomeNullableDateTimeOffset))] [InlineData(nameof(FilterableResource.SomeNullableTimeSpan))] + [InlineData(nameof(FilterableResource.SomeNullableDateOnly))] + [InlineData(nameof(FilterableResource.SomeNullableTimeOnly))] [InlineData(nameof(FilterableResource.SomeNullableEnum))] public async Task Can_filter_is_null_on_type(string propertyName) { @@ -303,6 +370,8 @@ public async Task Can_filter_is_null_on_type(string propertyName) SomeNullableDateTime = 1.January(2001).AsUtc(), SomeNullableDateTimeOffset = 1.January(2001).AsUtc(), SomeNullableTimeSpan = TimeSpan.FromHours(1), + SomeNullableDateOnly = DateOnly.FromDateTime(1.January(2001)), + SomeNullableTimeOnly = new TimeOnly(1, 0), SomeNullableEnum = DayOfWeek.Friday }; @@ -322,8 +391,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey(attributeName).With(value => value.Should().BeNull()); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey(attributeName).WhoseValue.Should().BeNull(); } [Theory] @@ -337,6 +406,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => [InlineData(nameof(FilterableResource.SomeNullableDateTime))] [InlineData(nameof(FilterableResource.SomeNullableDateTimeOffset))] [InlineData(nameof(FilterableResource.SomeNullableTimeSpan))] + [InlineData(nameof(FilterableResource.SomeNullableDateOnly))] + [InlineData(nameof(FilterableResource.SomeNullableTimeOnly))] [InlineData(nameof(FilterableResource.SomeNullableEnum))] public async Task Can_filter_is_not_null_on_type(string propertyName) { @@ -353,6 +424,8 @@ public async Task Can_filter_is_not_null_on_type(string propertyName) SomeNullableDateTime = 1.January(2001).AsUtc(), SomeNullableDateTimeOffset = 1.January(2001).AsUtc(), SomeNullableTimeSpan = TimeSpan.FromHours(1), + SomeNullableDateOnly = DateOnly.FromDateTime(1.January(2001)), + SomeNullableTimeOnly = new TimeOnly(1, 0), SomeNullableEnum = DayOfWeek.Friday }; @@ -372,7 +445,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey(attributeName).With(value => value.Should().NotBeNull()); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey(attributeName).WhoseValue.Should().NotBeNull(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs index 58277b3..0d02f23 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.Filtering; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class FilterDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class FilterDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim FilterableResources => Set(); } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs index fccf739..144fda5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterDepthTests.cs @@ -25,7 +25,7 @@ public FilterDepthTests(IntegrationTestContext posts = _fakers.BlogPost.Generate(2); + List posts = _fakers.BlogPost.GenerateList(2); posts[0].Caption = "One"; posts[1].Caption = "Two"; @@ -44,7 +44,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(posts[1].StringId); } @@ -60,7 +60,7 @@ public async Task Cannot_filter_on_ManyToOne_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -81,7 +81,7 @@ public async Task Cannot_filter_on_OneToMany_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -102,7 +102,7 @@ public async Task Cannot_filter_on_ManyToMany_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs index efc7075..e14889a 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterOperatorTests.cs @@ -33,6 +33,18 @@ public sealed class FilterOperatorTests : IClassFixture _testContext; public FilterOperatorTests(IntegrationTestContext testContext) @@ -68,8 +80,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someString").With(value => value.Should().Be(resource.SomeString)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someString").WhoseValue.Should().Be(resource.SomeString); + + responseDocument.Links.Should().NotBeNull(); + responseDocument.Links.Self.Should().Be("http://localhost/filterableResources?filter=equals(someString,'This%2c+that+%26+more+%2b+some')"); + responseDocument.Links.First.Should().Be("http://localhost/filterableResources?filter=equals(someString,%27This,%20that%20%26%20more%20%2B%20some%27)"); } [Fact] @@ -84,12 +100,13 @@ public async Task Cannot_filter_equality_on_two_attributes() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Comparing attributes against each other is not supported when using MongoDB."); error.Detail.Should().BeNull(); + error.Source.Should().BeNull(); } [Theory] @@ -130,8 +147,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someInt32").With(value => value.Should().Be(resource.SomeInt32)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someInt32").WhoseValue.Should().Be(resource.SomeInt32); } [Theory] @@ -172,8 +189,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDouble").With(value => value.Should().Be(resource.SomeDouble)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDouble").WhoseValue.Should().Be(resource.SomeDouble); } [Theory] @@ -222,10 +239,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDateTimeInUtcZone") - .With(value => value.Should().Be(resource.SomeDateTimeInUtcZone)); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateTimeInUtcZone").WhoseValue.Should().Be(resource.SomeDateTimeInUtcZone); } [Theory] @@ -274,9 +290,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someDateTimeOffset").With(value => value.Should().Be(resource.SomeDateTimeOffset)); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateTimeOffset").WhoseValue.Should().Be(resource.SomeDateTimeOffset); } [Theory] @@ -316,8 +332,98 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someTimeSpan").With(value => value.Should().Be(resource.SomeTimeSpan)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someTimeSpan").WhoseValue.Should().Be(resource.SomeTimeSpan); + } + + [Theory] + [InlineData(IsoDateOnlyLowerBound, IsoDateOnlyUpperBound, ComparisonOperator.LessThan, IsoDateOnlyInTheRange)] + [InlineData(IsoDateOnlyLowerBound, IsoDateOnlyUpperBound, ComparisonOperator.LessThan, IsoDateOnlyUpperBound)] + [InlineData(IsoDateOnlyLowerBound, IsoDateOnlyUpperBound, ComparisonOperator.LessOrEqual, IsoDateOnlyInTheRange)] + [InlineData(IsoDateOnlyLowerBound, IsoDateOnlyUpperBound, ComparisonOperator.LessOrEqual, IsoDateOnlyLowerBound)] + [InlineData(IsoDateOnlyUpperBound, IsoDateOnlyLowerBound, ComparisonOperator.GreaterThan, IsoDateOnlyInTheRange)] + [InlineData(IsoDateOnlyUpperBound, IsoDateOnlyLowerBound, ComparisonOperator.GreaterThan, IsoDateOnlyLowerBound)] + [InlineData(IsoDateOnlyUpperBound, IsoDateOnlyLowerBound, ComparisonOperator.GreaterOrEqual, IsoDateOnlyInTheRange)] + [InlineData(IsoDateOnlyUpperBound, IsoDateOnlyLowerBound, ComparisonOperator.GreaterOrEqual, IsoDateOnlyUpperBound)] + [InlineData(InvariantDateOnlyLowerBound, InvariantDateOnlyUpperBound, ComparisonOperator.LessThan, InvariantDateOnlyInTheRange)] + [InlineData(InvariantDateOnlyLowerBound, InvariantDateOnlyUpperBound, ComparisonOperator.LessThan, InvariantDateOnlyUpperBound)] + [InlineData(InvariantDateOnlyLowerBound, InvariantDateOnlyUpperBound, ComparisonOperator.LessOrEqual, InvariantDateOnlyInTheRange)] + [InlineData(InvariantDateOnlyLowerBound, InvariantDateOnlyUpperBound, ComparisonOperator.LessOrEqual, InvariantDateOnlyLowerBound)] + [InlineData(InvariantDateOnlyUpperBound, InvariantDateOnlyLowerBound, ComparisonOperator.GreaterThan, InvariantDateOnlyInTheRange)] + [InlineData(InvariantDateOnlyUpperBound, InvariantDateOnlyLowerBound, ComparisonOperator.GreaterThan, InvariantDateOnlyLowerBound)] + [InlineData(InvariantDateOnlyUpperBound, InvariantDateOnlyLowerBound, ComparisonOperator.GreaterOrEqual, InvariantDateOnlyInTheRange)] + [InlineData(InvariantDateOnlyUpperBound, InvariantDateOnlyLowerBound, ComparisonOperator.GreaterOrEqual, InvariantDateOnlyUpperBound)] + public async Task Can_filter_comparison_on_DateOnly(string matchingValue, string nonMatchingValue, ComparisonOperator filterOperator, string filterValue) + { + // Arrange + var resource = new FilterableResource + { + SomeDateOnly = DateOnly.Parse(matchingValue, CultureInfo.InvariantCulture) + }; + + var otherResource = new FilterableResource + { + SomeDateOnly = DateOnly.Parse(nonMatchingValue, CultureInfo.InvariantCulture) + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.FilterableResources.AddRange(resource, otherResource); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/filterableResources?filter={filterOperator.ToString().Camelize()}(someDateOnly,'{filterValue}')"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someDateOnly").WhoseValue.Should().Be(resource.SomeDateOnly); + } + + [Theory] + [InlineData(TimeOnlyLowerBound, TimeOnlyUpperBound, ComparisonOperator.LessThan, TimeOnlyInTheRange)] + [InlineData(TimeOnlyLowerBound, TimeOnlyUpperBound, ComparisonOperator.LessThan, TimeOnlyUpperBound)] + [InlineData(TimeOnlyLowerBound, TimeOnlyUpperBound, ComparisonOperator.LessOrEqual, TimeOnlyInTheRange)] + [InlineData(TimeOnlyLowerBound, TimeOnlyUpperBound, ComparisonOperator.LessOrEqual, TimeOnlyLowerBound)] + [InlineData(TimeOnlyUpperBound, TimeOnlyLowerBound, ComparisonOperator.GreaterThan, TimeOnlyInTheRange)] + [InlineData(TimeOnlyUpperBound, TimeOnlyLowerBound, ComparisonOperator.GreaterThan, TimeOnlyLowerBound)] + [InlineData(TimeOnlyUpperBound, TimeOnlyLowerBound, ComparisonOperator.GreaterOrEqual, TimeOnlyInTheRange)] + [InlineData(TimeOnlyUpperBound, TimeOnlyLowerBound, ComparisonOperator.GreaterOrEqual, TimeOnlyUpperBound)] + public async Task Can_filter_comparison_on_TimeOnly(string matchingValue, string nonMatchingValue, ComparisonOperator filterOperator, string filterValue) + { + // Arrange + var resource = new FilterableResource + { + SomeTimeOnly = TimeOnly.Parse(matchingValue, CultureInfo.InvariantCulture) + }; + + var otherResource = new FilterableResource + { + SomeTimeOnly = TimeOnly.Parse(nonMatchingValue, CultureInfo.InvariantCulture) + }; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.FilterableResources.AddRange(resource, otherResource); + await dbContext.SaveChangesAsync(); + }); + + string route = $"/filterableResources?filter={filterOperator.ToString().Camelize()}(someTimeOnly,'{filterValue}')"; + + // Act + (HttpResponseMessage httpResponse, Document responseDocument) = await _testContext.ExecuteGetAsync(route); + + // Assert + httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); + + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someTimeOnly").WhoseValue.Should().Be(resource.SomeTimeOnly); } [Theory] @@ -354,8 +460,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someString").With(value => value.Should().Be(resource.SomeString)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someString").WhoseValue.Should().Be(resource.SomeString); } [Theory] @@ -390,8 +496,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("someString").With(value => value.Should().Be(resource.SomeString)); + responseDocument.Data.ManyValue.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("someString").WhoseValue.Should().Be(resource.SomeString); } [Fact] @@ -406,7 +512,7 @@ public async Task Cannot_filter_on_has() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -427,7 +533,7 @@ public async Task Cannot_filter_on_has_with_nested_condition() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -448,7 +554,7 @@ public async Task Cannot_filter_on_count() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -494,7 +600,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(resource1.StringId); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs index c432a77..d572e4d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterTests.cs @@ -24,7 +24,7 @@ public FilterTests(IntegrationTestContext public async Task Can_filter_on_ID() { // Arrange - List accounts = _fakers.WebAccount.Generate(2); + List accounts = _fakers.WebAccount.GenerateList(2); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -41,8 +41,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(accounts[0].StringId); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("userName").With(value => value.Should().Be(accounts[0].UserName)); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("userName").WhoseValue.Should().Be(accounts[0].UserName); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs index 17ec845..6554ce6 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Filtering/FilterableResource.cs @@ -79,12 +79,28 @@ public sealed class FilterableResource : HexStringMongoIdentifiable [Attr] public TimeSpan? SomeNullableTimeSpan { get; set; } + [Attr] + public DateOnly SomeDateOnly { get; set; } + + [Attr] + public DateOnly? SomeNullableDateOnly { get; set; } + + [Attr] + public TimeOnly SomeTimeOnly { get; set; } + + [Attr] + public TimeOnly? SomeNullableTimeOnly { get; set; } + [Attr] public DayOfWeek SomeEnum { get; set; } [Attr] public DayOfWeek? SomeNullableEnum { get; set; } + [HasOne] + [BsonIgnore] + public FilterableResource? Parent { get; set; } + [HasMany] [BsonIgnore] public ICollection Children { get; set; } = new List(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs index 2fcbfe5..e72fbfc 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Includes/IncludeTests.cs @@ -31,7 +31,7 @@ public async Task Cannot_include_in_primary_resources() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs index 96e031b..03567be 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/PaginationWithTotalCountTests.cs @@ -31,7 +31,7 @@ public PaginationWithTotalCountTests(IntegrationTestContext posts = _fakers.BlogPost.Generate(2); + List posts = _fakers.BlogPost.GenerateList(2); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -48,10 +48,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(posts[1].StringId); - responseDocument.Links.ShouldNotBeNull(); + responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be($"{HostPrefix}{route}"); responseDocument.Links.First.Should().Be($"{HostPrefix}/blogPosts?page%5Bsize%5D=1"); responseDocument.Links.Last.Should().Be($"{HostPrefix}/blogPosts?page%5Bnumber%5D=2&page%5Bsize%5D=1"); @@ -66,7 +66,7 @@ public async Task Uses_default_page_number_and_size() var options = (JsonApiOptions)_testContext.Factory.Services.GetRequiredService(); options.DefaultPageSize = new PageSize(2); - List posts = _fakers.BlogPost.Generate(3); + List posts = _fakers.BlogPost.GenerateList(3); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -83,11 +83,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(2); + responseDocument.Data.ManyValue.Should().HaveCount(2); responseDocument.Data.ManyValue[0].Id.Should().Be(posts[0].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(posts[1].StringId); - responseDocument.Links.ShouldNotBeNull(); + responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be($"{HostPrefix}{route}"); responseDocument.Links.First.Should().Be(responseDocument.Links.Self); responseDocument.Links.Last.Should().Be($"{HostPrefix}{route}?page%5Bnumber%5D=2"); @@ -102,7 +102,7 @@ public async Task Returns_all_resources_when_pagination_is_disabled() var options = (JsonApiOptions)_testContext.Factory.Services.GetRequiredService(); options.DefaultPageSize = null; - List posts = _fakers.BlogPost.Generate(25); + List posts = _fakers.BlogPost.GenerateList(25); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -119,9 +119,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(25); + responseDocument.Data.ManyValue.Should().HaveCount(25); - responseDocument.Links.ShouldNotBeNull(); + responseDocument.Links.Should().NotBeNull(); responseDocument.Links.Self.Should().Be($"{HostPrefix}{route}"); responseDocument.Links.First.Should().BeNull(); responseDocument.Links.Last.Should().BeNull(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs index 28a7c06..2790fb8 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Pagination/RangeValidationTests.cs @@ -24,7 +24,7 @@ public RangeValidationTests(IntegrationTestContext blogs = _fakers.Blog.Generate(3); + List blogs = _fakers.Blog.GenerateList(3); await _testContext.RunOnDatabaseAsync(async dbContext => { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs index a1e48b6..a80dda2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/QueryStringDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class QueryStringDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class QueryStringDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim Blogs => Set(); public MongoDbSetShim Posts => Set(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs index a5269d7..6c6fc94 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/Sorting/SortTests.cs @@ -26,7 +26,7 @@ public SortTests(IntegrationTestContext t public async Task Can_sort_in_primary_resources() { // Arrange - List posts = _fakers.BlogPost.Generate(3); + List posts = _fakers.BlogPost.GenerateList(3); posts[0].Caption = "B"; posts[1].Caption = "A"; posts[2].Caption = "C"; @@ -46,7 +46,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(3); + responseDocument.Data.ManyValue.Should().HaveCount(3); responseDocument.Data.ManyValue[0].Id.Should().Be(posts[1].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(posts[0].StringId); responseDocument.Data.ManyValue[2].Id.Should().Be(posts[2].StringId); @@ -64,7 +64,7 @@ public async Task Cannot_sort_on_OneToMany_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -85,7 +85,7 @@ public async Task Cannot_sort_on_ManyToMany_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -106,7 +106,7 @@ public async Task Cannot_sort_on_ManyToOne_relationship() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -119,7 +119,7 @@ public async Task Cannot_sort_on_ManyToOne_relationship() public async Task Can_sort_descending_by_ID() { // Arrange - List accounts = _fakers.WebAccount.Generate(3); + List accounts = _fakers.WebAccount.GenerateList(3); accounts[0].Id = "5ff752c4f7c9a9a8373991b2"; accounts[1].Id = "5ff752c3f7c9a9a8373991b1"; accounts[2].Id = "5ff752c2f7c9a9a8373991b0"; @@ -143,7 +143,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(3); + responseDocument.Data.ManyValue.Should().HaveCount(3); responseDocument.Data.ManyValue[0].Id.Should().Be(accounts[1].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(accounts[2].StringId); responseDocument.Data.ManyValue[2].Id.Should().Be(accounts[0].StringId); @@ -153,7 +153,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Sorts_by_ID_if_none_specified() { // Arrange - List accounts = _fakers.WebAccount.Generate(4); + List accounts = _fakers.WebAccount.GenerateList(4); accounts[0].Id = "5ff8a7bcb2a9b83724282718"; accounts[1].Id = "5ff8a7bcb2a9b83724282717"; accounts[2].Id = "5ff8a7bbb2a9b83724282716"; @@ -174,7 +174,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(4); + responseDocument.Data.ManyValue.Should().HaveCount(4); responseDocument.Data.ManyValue[0].Id.Should().Be(accounts[2].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(accounts[1].StringId); responseDocument.Data.ManyValue[2].Id.Should().Be(accounts[0].StringId); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs index f00897c..80fc442 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/ResultCapturingRepository.cs @@ -14,8 +14,9 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.QueryStrings.SparseFiel public sealed class ResultCapturingRepository( IMongoDataAccess mongoDataAccess, ITargetedFields targetedFields, IResourceGraph resourceGraph, IResourceFactory resourceFactory, IEnumerable constraintProviders, IResourceDefinitionAccessor resourceDefinitionAccessor, IQueryableBuilder queryableBuilder, - ResourceCaptureStore captureStore) : MongoRepository(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, - resourceDefinitionAccessor, queryableBuilder) + ResourceCaptureStore captureStore) + : MongoRepository(mongoDataAccess, targetedFields, resourceGraph, resourceFactory, constraintProviders, resourceDefinitionAccessor, + queryableBuilder) where TResource : class, IIdentifiable { private readonly ResourceCaptureStore _captureStore = captureStore; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs index 8b94f13..8879c97 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/SparseFieldSets/SparseFieldSetTests.cs @@ -3,7 +3,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using TestBuildingBlocks; using Xunit; @@ -26,11 +25,11 @@ public SparseFieldSetTests(IntegrationTestContext { - services.TryAddSingleton(); - services.AddResourceRepository>(); services.AddResourceRepository>(); services.AddResourceRepository>(); + + services.AddSingleton(); }); } @@ -46,7 +45,7 @@ public async Task Cannot_select_fields_with_relationship_in_primary_resources() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -62,7 +61,7 @@ public async Task Can_select_attribute_in_primary_resources() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -79,10 +78,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(post.StringId); - responseDocument.Data.ManyValue[0].Attributes.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(post.Caption)); + responseDocument.Data.ManyValue[0].Attributes.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("caption").WhoseValue.Should().Be(post.Caption); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; @@ -102,7 +101,7 @@ public async Task Cannot_select_relationship_in_primary_resources() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -118,7 +117,7 @@ public async Task Can_select_attribute_in_primary_resource_by_ID() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -134,10 +133,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(post.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(1); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("url").With(value => value.Should().Be(post.Url)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(1); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("url").WhoseValue.Should().Be(post.Url); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; @@ -149,7 +148,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_select_fields_of_ManyToOne_relationship() { // Arrange - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -165,7 +164,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -178,7 +177,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_select_fields_of_OneToMany_relationship() { // Arrange - WebAccount account = _fakers.WebAccount.Generate(); + WebAccount account = _fakers.WebAccount.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -194,7 +193,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -207,7 +206,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_select_fields_of_ManyToMany_relationship() { // Arrange - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -223,7 +222,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -239,7 +238,7 @@ public async Task Can_select_ID() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -256,10 +255,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(post.StringId); - responseDocument.Data.ManyValue[0].Attributes.ShouldHaveCount(1); - responseDocument.Data.ManyValue[0].Attributes.ShouldContainKey("caption").With(value => value.Should().Be(post.Caption)); + responseDocument.Data.ManyValue[0].Attributes.Should().HaveCount(1); + responseDocument.Data.ManyValue[0].Attributes.Should().ContainKey("caption").WhoseValue.Should().Be(post.Caption); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); var postCaptured = (BlogPost)store.Resources.Should().ContainSingle(resource => resource is BlogPost).Which; @@ -275,7 +274,7 @@ public async Task Can_select_empty_fieldset() var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - BlogPost post = _fakers.BlogPost.Generate(); + BlogPost post = _fakers.BlogPost.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -292,7 +291,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(post.StringId); responseDocument.Data.ManyValue[0].Attributes.Should().BeNull(); responseDocument.Data.ManyValue[0].Relationships.Should().BeNull(); @@ -309,7 +308,7 @@ public async Task Fetches_all_scalar_properties_when_fieldset_contains_readonly_ var store = _testContext.Factory.Services.GetRequiredService(); store.Clear(); - Blog blog = _fakers.Blog.Generate(); + Blog blog = _fakers.Blog.GenerateOne(); blog.IsPublished = true; await _testContext.RunOnDatabaseAsync(async dbContext => @@ -326,10 +325,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(blog.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(1); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("showAdvertisements").With(value => value.Should().Be(blog.ShowAdvertisements)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(1); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("showAdvertisements").WhoseValue.Should().Be(blog.ShowAdvertisements); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); var blogCaptured = (Blog)store.Resources.Should().ContainSingle(resource => resource is Blog).Which; diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs index de3c673..e3c2e1c 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/QueryStrings/WebAccount.cs @@ -12,7 +12,7 @@ public sealed class WebAccount : HexStringMongoIdentifiable [Attr] public string UserName { get; set; } = null!; - [Attr(Capabilities = ~AttrCapabilities.AllowView)] + [Attr(Capabilities = AttrCapabilities.All & ~AttrCapabilities.AllowView)] public string Password { get; set; } = null!; [Attr] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index ccc1ec6..36006e2 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -26,7 +26,7 @@ public CreateResourceTests(IntegrationTestContext value.Should().Be(newWorkItem.Description)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("dueAt").With(value => value.Should().Be(newWorkItem.DueAt)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(newWorkItem.Description); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().Be(newWorkItem.DueAt); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); - string newWorkItemId = responseDocument.Data.SingleValue.Id.ShouldNotBeNull(); + string newWorkItemId = responseDocument.Data.SingleValue.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -90,7 +90,7 @@ public async Task Cannot_create_resource_with_int_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.InternalServerError); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.InternalServerError); @@ -124,13 +124,13 @@ public async Task Can_create_resource_without_attributes_or_relationships() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Created); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItems"); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("description").With(value => value.Should().BeNull()); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("dueAt").With(value => value.Should().BeNull()); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("description").WhoseValue.Should().BeNull(); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().BeNull(); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); - string newWorkItemId = responseDocument.Data.SingleValue.Id.ShouldNotBeNull(); + string newWorkItemId = responseDocument.Data.SingleValue.Id.Should().NotBeNull().And.Subject; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -166,14 +166,14 @@ public async Task Cannot_create_resource_with_client_generated_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Forbidden); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.Forbidden); error.Title.Should().Be("Failed to deserialize request body: The use of client-generated IDs is disabled."); error.Detail.Should().BeNull(); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Pointer.Should().Be("/data/id"); - error.Meta.ShouldContainKey("requestBody").With(value => value.ShouldNotBeNull().ToString().ShouldNotBeEmpty()); + error.Meta.Should().HaveRequestBody(); } } diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index c9c732a..1f40db8 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -32,7 +32,7 @@ public CreateResourceWithClientGeneratedIdTests(IntegrationTestContext value.Should().Be(groupName)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(groupName); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -76,7 +76,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_create_resource_with_client_generated_string_ID_having_side_effects_with_fieldset() { // Arrange - WorkItemGroup newGroup = _fakers.WorkItemGroup.Generate(); + WorkItemGroup newGroup = _fakers.WorkItemGroup.GenerateOne(); newGroup.Id = "free-format-client-generated-id-2"; var requestBody = new @@ -102,11 +102,11 @@ public async Task Can_create_resource_with_client_generated_string_ID_having_sid string groupName = $"{newGroup.Name}{ImplicitlyChangingWorkItemGroupDefinition.Suffix}"; - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItemGroups"); responseDocument.Data.SingleValue.Id.Should().Be(newGroup.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(1); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(groupName)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(1); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(groupName); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -121,7 +121,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_create_resource_with_client_generated_string_ID_having_no_side_effects() { // Arrange - RgbColor newColor = _fakers.RgbColor.Generate(); + RgbColor newColor = _fakers.RgbColor.GenerateOne(); newColor.Id = "free-format-client-generated-id-3"; var requestBody = new @@ -159,7 +159,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_create_resource_with_client_generated_string_ID_having_no_side_effects_with_fieldset() { // Arrange - RgbColor newColor = _fakers.RgbColor.Generate(); + RgbColor newColor = _fakers.RgbColor.GenerateOne(); newColor.Id = "free-format-client-generated-id-4"; var requestBody = new @@ -197,10 +197,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_create_resource_for_existing_client_generated_ID() { // Arrange - RgbColor existingColor = _fakers.RgbColor.Generate(); + RgbColor existingColor = _fakers.RgbColor.GenerateOne(); existingColor.Id = "free-format-client-generated-id-5"; - string newDisplayName = _fakers.RgbColor.Generate().DisplayName; + string newDisplayName = _fakers.RgbColor.GenerateOne().DisplayName; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -229,7 +229,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.Conflict); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.Conflict); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs index e0d92de..c560e56 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToManyRelationshipTests.cs @@ -24,7 +24,7 @@ public CreateResourceWithToManyRelationshipTests(IntegrationTestContext { @@ -62,7 +62,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs index be6732c..12d5711 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithToOneRelationshipTests.cs @@ -24,10 +24,10 @@ public CreateResourceWithToOneRelationshipTests(IntegrationTestContext { @@ -67,7 +67,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs index 3e1bb87..70e3307 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Deleting/DeleteResourceTests.cs @@ -24,7 +24,7 @@ public DeleteResourceTests(IntegrationTestContext { @@ -44,9 +44,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => await _testContext.RunOnDatabaseAsync(async dbContext => { - WorkItem? workItemsInDatabase = await dbContext.WorkItems.FirstWithIdOrDefaultAsync(existingWorkItem.Id); + WorkItem? workItemInDatabase = await dbContext.WorkItems.FirstWithIdOrDefaultAsync(existingWorkItem.Id); - workItemsInDatabase.Should().BeNull(); + workItemInDatabase.Should().BeNull(); }); } @@ -64,7 +64,7 @@ public async Task Cannot_delete_unknown_resource() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs index 6a9e32c..af5dee5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchRelationshipTests.cs @@ -24,7 +24,7 @@ public FetchRelationshipTests(IntegrationTestContext { @@ -40,7 +40,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -53,7 +53,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_get_OneToMany_relationship() { // Arrange - UserAccount userAccount = _fakers.UserAccount.Generate(); + UserAccount userAccount = _fakers.UserAccount.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -69,7 +69,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -82,7 +82,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_get_ManyToMany_relationship() { // Arrange - WorkItem workItem = _fakers.WorkItem.Generate(); + WorkItem workItem = _fakers.WorkItem.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -98,7 +98,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs index 39a52d6..dc2aa26 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Fetching/FetchResourceTests.cs @@ -25,7 +25,7 @@ public FetchResourceTests(IntegrationTestContext workItems = _fakers.WorkItem.Generate(2); + List workItems = _fakers.WorkItem.GenerateList(2); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -42,20 +42,20 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(2); + responseDocument.Data.ManyValue.Should().HaveCount(2); ResourceObject item1 = responseDocument.Data.ManyValue.Single(resource => resource.Id == workItems[0].StringId); item1.Type.Should().Be("workItems"); - item1.Attributes.ShouldContainKey("description").With(value => value.Should().Be(workItems[0].Description)); - item1.Attributes.ShouldContainKey("dueAt").With(value => value.Should().Be(workItems[0].DueAt)); - item1.Attributes.ShouldContainKey("priority").With(value => value.Should().Be(workItems[0].Priority)); + item1.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(workItems[0].Description); + item1.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().Be(workItems[0].DueAt); + item1.Attributes.Should().ContainKey("priority").WhoseValue.Should().Be(workItems[0].Priority); item1.Relationships.Should().BeNull(); ResourceObject item2 = responseDocument.Data.ManyValue.Single(resource => resource.Id == workItems[1].StringId); item2.Type.Should().Be("workItems"); - item2.Attributes.ShouldContainKey("description").With(value => value.Should().Be(workItems[1].Description)); - item2.Attributes.ShouldContainKey("dueAt").With(value => value.Should().Be(workItems[1].DueAt)); - item2.Attributes.ShouldContainKey("priority").With(value => value.Should().Be(workItems[1].Priority)); + item2.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(workItems[1].Description); + item2.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().Be(workItems[1].DueAt); + item2.Attributes.Should().ContainKey("priority").WhoseValue.Should().Be(workItems[1].Priority); item2.Relationships.Should().BeNull(); } @@ -63,7 +63,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_get_primary_resource_by_ID() { // Arrange - WorkItem workItem = _fakers.WorkItem.Generate(); + WorkItem workItem = _fakers.WorkItem.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -79,12 +79,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItems"); responseDocument.Data.SingleValue.Id.Should().Be(workItem.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("description").With(value => value.Should().Be(workItem.Description)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("dueAt").With(value => value.Should().Be(workItem.DueAt)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("priority").With(value => value.Should().Be(workItem.Priority)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(workItem.Description); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().Be(workItem.DueAt); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("priority").WhoseValue.Should().Be(workItem.Priority); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); } @@ -102,7 +102,7 @@ public async Task Cannot_get_primary_resource_for_unknown_ID() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); @@ -114,7 +114,7 @@ public async Task Cannot_get_primary_resource_for_unknown_ID() public async Task Cannot_get_secondary_ManyToOne_resource() { // Arrange - WorkItem workItem = _fakers.WorkItem.Generate(); + WorkItem workItem = _fakers.WorkItem.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -130,7 +130,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -143,7 +143,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_get_secondary_OneToMany_resources() { // Arrange - UserAccount userAccount = _fakers.UserAccount.Generate(); + UserAccount userAccount = _fakers.UserAccount.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -159,7 +159,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -172,7 +172,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_get_secondary_ManyToMany_resources() { // Arrange - WorkItem workItem = _fakers.WorkItem.Generate(); + WorkItem workItem = _fakers.WorkItem.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -188,7 +188,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs index 32a0dd9..6cd2ad0 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/ReadWriteDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ReadWrite; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ReadWriteDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class ReadWriteDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim WorkItems => Set(); public MongoDbSetShim WorkTags => Set(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs index c2fd820..0e60f81 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/AddToToManyRelationshipTests.cs @@ -24,8 +24,8 @@ public AddToToManyRelationshipTests(IntegrationTestContext { @@ -54,7 +54,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -67,8 +67,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_add_to_ManyToMany_relationship() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - WorkTag existingTag = _fakers.WorkTag.Generate(); + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + WorkTag existingTag = _fakers.WorkTag.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -97,7 +97,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs index eab8111..aeb987d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/RemoveFromToManyRelationshipTests.cs @@ -24,8 +24,8 @@ public RemoveFromToManyRelationshipTests(IntegrationTestContext { @@ -54,7 +54,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -67,8 +67,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_remove_from_ManyToMany_relationship() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - existingWorkItem.Tags = _fakers.WorkTag.Generate(1).ToHashSet(); + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + existingWorkItem.Tags = _fakers.WorkTag.GenerateSet(1); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -97,7 +97,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs index 9df3deb..7d22d4d 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/ReplaceToManyRelationshipTests.cs @@ -24,8 +24,8 @@ public ReplaceToManyRelationshipTests(IntegrationTestContext { @@ -54,7 +54,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -67,8 +67,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_replace_ManyToMany_relationship() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - WorkTag existingTag = _fakers.WorkTag.Generate(); + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + WorkTag existingTag = _fakers.WorkTag.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -97,7 +97,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs index ddd0f1d..545ff89 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Relationships/UpdateToOneRelationshipTests.cs @@ -24,8 +24,8 @@ public UpdateToOneRelationshipTests(IntegrationTestContext { @@ -51,7 +51,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs index f7fa710..005c180 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/ReplaceToManyRelationshipTests.cs @@ -24,8 +24,8 @@ public ReplaceToManyRelationshipTests(IntegrationTestContext { @@ -65,7 +65,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -78,8 +78,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Cannot_replace_ManyToMany_relationship() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - WorkTag existingTag = _fakers.WorkTag.Generate(); + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + WorkTag existingTag = _fakers.WorkTag.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -119,7 +119,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index 672b836..123fa48 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -34,7 +34,7 @@ public UpdateResourceTests(IntegrationTestContext { @@ -80,8 +80,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_partially_update_resource_with_string_ID() { // Arrange - WorkItemGroup existingGroup = _fakers.WorkItemGroup.Generate(); - string newName = _fakers.WorkItemGroup.Generate().Name; + WorkItemGroup existingGroup = _fakers.WorkItemGroup.GenerateOne(); + string newName = _fakers.WorkItemGroup.GenerateOne().Name; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -112,11 +112,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string groupName = $"{newName}{ImplicitlyChangingWorkItemGroupDefinition.Suffix}"; - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItemGroups"); responseDocument.Data.SingleValue.Id.Should().Be(existingGroup.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(groupName)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("isPublic").With(value => value.Should().Be(existingGroup.IsPublic)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(groupName); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("isPublic").WhoseValue.Should().Be(existingGroup.IsPublic); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -132,8 +132,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_completely_update_resource_with_string_ID() { // Arrange - RgbColor existingColor = _fakers.RgbColor.Generate(); - string newDisplayName = _fakers.RgbColor.Generate().DisplayName; + RgbColor existingColor = _fakers.RgbColor.GenerateOne(); + string newDisplayName = _fakers.RgbColor.GenerateOne().DisplayName; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -176,8 +176,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_update_resource_without_side_effects() { // Arrange - UserAccount existingUserAccount = _fakers.UserAccount.Generate(); - UserAccount newUserAccount = _fakers.UserAccount.Generate(); + UserAccount existingUserAccount = _fakers.UserAccount.GenerateOne(); + UserAccount newUserAccount = _fakers.UserAccount.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -222,8 +222,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_update_resource_with_side_effects() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - string newDescription = _fakers.WorkItem.Generate().Description!; + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + string newDescription = _fakers.WorkItem.GenerateOne().Description!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -255,13 +255,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string itemDescription = $"{newDescription}{ImplicitlyChangingWorkItemDefinition.Suffix}"; - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItems"); responseDocument.Data.SingleValue.Id.Should().Be(existingWorkItem.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("description").With(value => value.Should().Be(itemDescription)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("dueAt").With(value => value.Should().BeNull()); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("priority").With(value => value.Should().Be(existingWorkItem.Priority)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("isImportant").With(value => value.Should().Be(existingWorkItem.IsImportant)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(itemDescription); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("dueAt").WhoseValue.Should().BeNull(); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("priority").WhoseValue.Should().Be(existingWorkItem.Priority); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("isImportant").WhoseValue.Should().Be(existingWorkItem.IsImportant); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -278,8 +278,8 @@ await _testContext.RunOnDatabaseAsync(async dbContext => public async Task Can_update_resource_with_side_effects_with_primary_fieldset() { // Arrange - WorkItem existingWorkItem = _fakers.WorkItem.Generate(); - string newDescription = _fakers.WorkItem.Generate().Description!; + WorkItem existingWorkItem = _fakers.WorkItem.GenerateOne(); + string newDescription = _fakers.WorkItem.GenerateOne().Description!; await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -311,12 +311,12 @@ await _testContext.RunOnDatabaseAsync(async dbContext => string itemDescription = $"{newDescription}{ImplicitlyChangingWorkItemDefinition.Suffix}"; - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Type.Should().Be("workItems"); responseDocument.Data.SingleValue.Id.Should().Be(existingWorkItem.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(2); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("description").With(value => value.Should().Be(itemDescription)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("priority").With(value => value.Should().Be(existingWorkItem.Priority)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(2); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("description").WhoseValue.Should().Be(itemDescription); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("priority").WhoseValue.Should().Be(existingWorkItem.Priority); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => @@ -352,7 +352,7 @@ public async Task Cannot_update_resource_on_unknown_resource_ID_in_url() // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.NotFound); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.NotFound); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs index c1e99fc..1f14cc3 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateToOneRelationshipTests.cs @@ -24,8 +24,8 @@ public UpdateToOneRelationshipTests(IntegrationTestContext { @@ -62,7 +62,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs index 4eecb06..bd13296 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ReadWrite/WorkItem.cs @@ -18,7 +18,7 @@ public sealed class WorkItem : HexStringMongoIdentifiable [Attr] public WorkItemPriority Priority { get; set; } - [Attr(Capabilities = ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] + [Attr(Capabilities = AttrCapabilities.All & ~(AttrCapabilities.AllowCreate | AttrCapabilities.AllowChange))] [BsonIgnore] public bool IsImportant { diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs index df3364e..e05f114 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/ResourceDefinitionReadTests.cs @@ -3,7 +3,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Serialization.Objects; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using TestBuildingBlocks; using Xunit; @@ -26,12 +25,12 @@ public ResourceDefinitionReadTests(IntegrationTestContext { - services.TryAddSingleton(); - services.TryAddSingleton(); - - services.AddResourceDefinition(); - services.AddResourceDefinition(); services.AddResourceDefinition(); + services.AddResourceDefinition(); + services.AddResourceDefinition(); + + services.AddSingleton(); + services.AddSingleton(); }); var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); @@ -53,7 +52,7 @@ public async Task Filter_from_resource_definition_is_applied() var settingsProvider = (TestClientSettingsProvider)_testContext.Factory.Services.GetRequiredService(); settingsProvider.HidePlanetsWithPrivateName(); - List planets = _fakers.Planet.Generate(4); + List planets = _fakers.Planet.GenerateList(4); planets[0].PrivateName = "A"; planets[2].PrivateName = "B"; @@ -72,7 +71,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(2); + responseDocument.Data.ManyValue.Should().HaveCount(2); responseDocument.Data.ManyValue[0].Id.Should().Be(planets[1].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(planets[3].StringId); @@ -101,7 +100,7 @@ public async Task Filter_from_resource_definition_and_query_string_are_applied() var settingsProvider = (TestClientSettingsProvider)_testContext.Factory.Services.GetRequiredService(); settingsProvider.HidePlanetsWithPrivateName(); - List planets = _fakers.Planet.Generate(4); + List planets = _fakers.Planet.GenerateList(4); planets[0].HasRingSystem = true; planets[0].PrivateName = "A"; @@ -128,7 +127,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(planets[3].StringId); responseDocument.Meta.Should().ContainTotal(1); @@ -152,7 +151,7 @@ public async Task Sort_from_resource_definition_is_applied() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List stars = _fakers.Star.Generate(3); + List stars = _fakers.Star.GenerateList(3); stars[0].SolarMass = 500m; stars[0].SolarRadius = 1m; @@ -178,7 +177,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(3); + responseDocument.Data.ManyValue.Should().HaveCount(3); responseDocument.Data.ManyValue[0].Id.Should().Be(stars[1].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(stars[0].StringId); responseDocument.Data.ManyValue[2].Id.Should().Be(stars[2].StringId); @@ -204,7 +203,7 @@ public async Task Sort_from_query_string_is_applied() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List stars = _fakers.Star.Generate(3); + List stars = _fakers.Star.GenerateList(3); stars[0].Name = "B"; stars[0].SolarRadius = 10m; @@ -230,7 +229,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(3); + responseDocument.Data.ManyValue.Should().HaveCount(3); responseDocument.Data.ManyValue[0].Id.Should().Be(stars[2].StringId); responseDocument.Data.ManyValue[1].Id.Should().Be(stars[0].StringId); responseDocument.Data.ManyValue[2].Id.Should().Be(stars[1].StringId); @@ -256,7 +255,7 @@ public async Task Page_size_from_resource_definition_is_applied() // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List stars = _fakers.Star.Generate(10); + List stars = _fakers.Star.GenerateList(10); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -273,7 +272,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(5); + responseDocument.Data.ManyValue.Should().HaveCount(5); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] { @@ -298,7 +297,7 @@ public async Task Attribute_inclusion_from_resource_definition_is_applied_for_om // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - Star star = _fakers.Star.Generate(); + Star star = _fakers.Star.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -314,10 +313,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(star.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(star.Name)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("kind").With(value => value.Should().Be(star.Kind)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(star.Name); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("kind").WhoseValue.Should().Be(star.Kind); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] @@ -338,7 +337,7 @@ public async Task Attribute_inclusion_from_resource_definition_is_applied_for_fi // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - Star star = _fakers.Star.Generate(); + Star star = _fakers.Star.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -354,11 +353,11 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(star.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(2); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(star.Name)); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("solarRadius").With(value => value.Should().Be(star.SolarRadius)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(2); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(star.Name); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("solarRadius").WhoseValue.Should().Be(star.SolarRadius); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] @@ -379,7 +378,7 @@ public async Task Attribute_exclusion_from_resource_definition_is_applied_for_om // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - Star star = _fakers.Star.Generate(); + Star star = _fakers.Star.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -395,9 +394,9 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(star.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(star.Name)); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(star.Name); responseDocument.Data.SingleValue.Attributes.Should().NotContainKey("isVisibleFromEarth"); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); @@ -419,7 +418,7 @@ public async Task Attribute_exclusion_from_resource_definition_is_applied_for_fi // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - Star star = _fakers.Star.Generate(); + Star star = _fakers.Star.GenerateOne(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -435,10 +434,10 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.SingleValue.ShouldNotBeNull(); + responseDocument.Data.SingleValue.Should().NotBeNull(); responseDocument.Data.SingleValue.Id.Should().Be(star.StringId); - responseDocument.Data.SingleValue.Attributes.ShouldHaveCount(1); - responseDocument.Data.SingleValue.Attributes.ShouldContainKey("name").With(value => value.Should().Be(star.Name)); + responseDocument.Data.SingleValue.Attributes.Should().HaveCount(1); + responseDocument.Data.SingleValue.Attributes.Should().ContainKey("name").WhoseValue.Should().Be(star.Name); responseDocument.Data.SingleValue.Relationships.Should().BeNull(); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] @@ -459,7 +458,7 @@ public async Task Queryable_parameter_handler_from_resource_definition_is_applie // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List moons = _fakers.Moon.Generate(2); + List moons = _fakers.Moon.GenerateList(2); moons[0].SolarRadius = .5m; moons[1].SolarRadius = 50m; @@ -478,7 +477,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(moons[1].StringId); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] @@ -502,7 +501,7 @@ public async Task Queryable_parameter_handler_from_resource_definition_and_query // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - List moons = _fakers.Moon.Generate(4); + List moons = _fakers.Moon.GenerateList(4); moons[0].Name = "Alpha1"; moons[0].SolarRadius = 1m; @@ -531,7 +530,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.OK); - responseDocument.Data.ManyValue.ShouldHaveCount(1); + responseDocument.Data.ManyValue.Should().HaveCount(1); responseDocument.Data.ManyValue[0].Id.Should().Be(moons[2].StringId); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] @@ -555,8 +554,8 @@ public async Task Queryable_parameter_handler_from_resource_definition_is_not_ap // Arrange var hitCounter = _testContext.Factory.Services.GetRequiredService(); - Planet planet = _fakers.Planet.Generate(); - planet.Moons = _fakers.Moon.Generate(1).ToHashSet(); + Planet planet = _fakers.Planet.GenerateOne(); + planet.Moons = _fakers.Moon.GenerateSet(1); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -572,13 +571,13 @@ await _testContext.RunOnDatabaseAsync(async dbContext => // Assert httpResponse.ShouldHaveStatusCode(HttpStatusCode.BadRequest); - responseDocument.Errors.ShouldHaveCount(1); + responseDocument.Errors.Should().HaveCount(1); ErrorObject error = responseDocument.Errors[0]; error.StatusCode.Should().Be(HttpStatusCode.BadRequest); error.Title.Should().Be("Custom query string parameters cannot be used on nested resource endpoints."); error.Detail.Should().Be("Query string parameter 'isLargerThanTheSun' cannot be used on a nested resource endpoint."); - error.Source.ShouldNotBeNull(); + error.Source.Should().NotBeNull(); error.Source.Parameter.Should().Be("isLargerThanTheSun"); hitCounter.HitExtensibilityPoints.Should().BeEquivalentTo(new[] diff --git a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs index 901ce69..417d995 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs +++ b/test/JsonApiDotNetCoreMongoDbTests/IntegrationTests/ResourceDefinitions/Reading/UniverseDbContext.cs @@ -5,7 +5,8 @@ namespace JsonApiDotNetCoreMongoDbTests.IntegrationTests.ResourceDefinitions.Reading; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class UniverseDbContext(IMongoDatabase database) : MongoDbContextShim(database) +public sealed class UniverseDbContext(IMongoDatabase database) + : MongoDbContextShim(database) { public MongoDbSetShim Stars => Set(); public MongoDbSetShim Planets => Set(); diff --git a/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj b/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj index d25aea3..22338d5 100644 --- a/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj +++ b/test/JsonApiDotNetCoreMongoDbTests/JsonApiDotNetCoreMongoDbTests.csproj @@ -1,6 +1,6 @@ - net8.0;net6.0 + net9.0;net8.0 diff --git a/test/TestBuildingBlocks/FakerExtensions.cs b/test/TestBuildingBlocks/FakerExtensions.cs index aff1cd0..75714e6 100644 --- a/test/TestBuildingBlocks/FakerExtensions.cs +++ b/test/TestBuildingBlocks/FakerExtensions.cs @@ -1,14 +1,13 @@ using System.Diagnostics; using System.Reflection; using Bogus; -using FluentAssertions.Extensions; using Xunit; namespace TestBuildingBlocks; public static class FakerExtensions { - public static Faker MakeDeterministic(this Faker faker) + public static Faker MakeDeterministic(this Faker faker, DateTime? systemTimeUtc = null) where T : class { int seed = GetFakerSeed(); @@ -16,7 +15,7 @@ public static Faker MakeDeterministic(this Faker faker) // Setting the system DateTime to kind Utc, so that faker calls like PastOffset() don't depend on the system time zone. // See https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset.op_implicit?view=net-6.0#remarks - faker.UseDateTimeReference(1.January(2020).At(1, 1, 1).AsUtc()); + faker.UseDateTimeReference(systemTimeUtc ?? IntegrationTest.DefaultDateTimeUtc.UtcDateTime); return faker; } @@ -80,4 +79,27 @@ private static int GetDeterministicHashCode(string source) return hash1 + hash2 * 1566083941; } } + + // The methods below exist so that a non-nullable return type is inferred. + // The Bogus NuGet package is not annotated for nullable reference types. + + public static T GenerateOne(this Faker faker) + where T : class + { + return faker.Generate(); + } + +#pragma warning disable AV1130 // Return type in method signature should be an interface to an unchangeable collection + public static List GenerateList(this Faker faker, int count) + where T : class + { + return faker.Generate(count); + } + + public static HashSet GenerateSet(this Faker faker, int count) + where T : class + { + return faker.Generate(count).ToHashSet(); + } +#pragma warning restore AV1130 // Return type in method signature should be an interface to an unchangeable collection } diff --git a/test/TestBuildingBlocks/FluentExtensions.cs b/test/TestBuildingBlocks/FluentExtensions.cs new file mode 100644 index 0000000..1ceebc0 --- /dev/null +++ b/test/TestBuildingBlocks/FluentExtensions.cs @@ -0,0 +1,45 @@ +using FluentAssertions; +using FluentAssertions.Numeric; +using FluentAssertions.Primitives; +using JetBrains.Annotations; +using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; + +// ReSharper disable UnusedMethodReturnValue.Global + +namespace TestBuildingBlocks; + +public static class FluentExtensions +{ + private const decimal NumericPrecision = 0.00000000001M; + + /// + /// Same as , but with + /// default precision. + /// + [CustomAssertion] + public static AndConstraint> BeApproximately(this NullableNumericAssertions parent, decimal? expectedValue, + string because = "", params object[] becauseArgs) + { + return parent.BeApproximately(expectedValue, NumericPrecision, because, becauseArgs); + } + + // Workaround for source.Should().NotBeNull().And.Subject having declared type 'object'. + [System.Diagnostics.Contracts.Pure] + public static StrongReferenceTypeAssertions RefShould([SysNotNull] this T? actualValue) + where T : class + { + actualValue.Should().NotBeNull(); + return new StrongReferenceTypeAssertions(actualValue); + } + + public static void With(this T subject, [InstantHandle] Action continuation) + { + continuation(subject); + } + + public sealed class StrongReferenceTypeAssertions(TReference subject) + : ReferenceTypeAssertions>(subject) + { + protected override string Identifier => "subject"; + } +} diff --git a/test/TestBuildingBlocks/FluentMetaExtensions.cs b/test/TestBuildingBlocks/FluentMetaExtensions.cs new file mode 100644 index 0000000..7ed040c --- /dev/null +++ b/test/TestBuildingBlocks/FluentMetaExtensions.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using FluentAssertions; +using FluentAssertions.Collections; + +namespace TestBuildingBlocks; + +public static class FluentMetaExtensions +{ + /// + /// Asserts that a "meta" dictionary contains a single element named "total" with the specified value. + /// + [CustomAssertion] +#pragma warning disable AV1553 // Do not use optional parameters with default value null for strings, collections or tasks + public static void ContainTotal(this GenericDictionaryAssertions, string, object?> source, int expected, + string? keyName = null) +#pragma warning restore AV1553 // Do not use optional parameters with default value null for strings, collections or tasks + { + JsonElement element = GetMetaJsonElement(source, keyName ?? "total"); + element.GetInt32().Should().Be(expected); + } + + /// + /// Asserts that a "meta" dictionary contains a single element named "requestBody" that isn't empty. + /// + [CustomAssertion] + public static void HaveRequestBody(this GenericDictionaryAssertions, string, object?> source) + { + JsonElement element = GetMetaJsonElement(source, "requestBody"); + element.ToString().Should().NotBeEmpty(); + } + + private static JsonElement GetMetaJsonElement(GenericDictionaryAssertions, string, object?> source, string metaKey) + { + object? value = source.ContainKey(metaKey).WhoseValue; + return value.Should().BeOfType().Subject; + } +} diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index 50fc3fd..e74c892 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -1,6 +1,7 @@ using System.Net.Http.Headers; using System.Text; using System.Text.Json; +using FluentAssertions.Extensions; using JsonApiDotNetCore.Middleware; using Xunit; @@ -12,14 +13,21 @@ namespace TestBuildingBlocks; /// public abstract class IntegrationTest : IAsyncLifetime { - private static readonly SemaphoreSlim ThrottleSemaphore; + private static readonly MediaTypeHeaderValue DefaultMediaType = MediaTypeHeaderValue.Parse(HeaderConstants.MediaType); + + private static readonly MediaTypeWithQualityHeaderValue OperationsMediaType = + MediaTypeWithQualityHeaderValue.Parse(HeaderConstants.AtomicOperationsMediaType); + + private static readonly SemaphoreSlim ThrottleSemaphore = GetDefaultThrottleSemaphore(); + + internal static DateTimeOffset DefaultDateTimeUtc { get; } = 1.January(2020).At(1, 2, 3).AsUtc(); protected abstract JsonSerializerOptions SerializerOptions { get; } - static IntegrationTest() + private static SemaphoreSlim GetDefaultThrottleSemaphore() { int maxConcurrentTestRuns = OperatingSystem.IsWindows() && Environment.GetEnvironmentVariable("CI") != null ? 32 : 64; - ThrottleSemaphore = new SemaphoreSlim(maxConcurrentTestRuns); + return new SemaphoreSlim(maxConcurrentTestRuns); } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteGetAsync(string requestUrl, @@ -28,32 +36,38 @@ static IntegrationTest() return await ExecuteRequestAsync(HttpMethod.Get, requestUrl, null, null, setRequestHeaders); } +#pragma warning disable AV1553 // Do not use optional parameters with default value null for strings, collections or tasks public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAsync(string requestUrl, - object requestBody, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) + object requestBody, string? contentType = null, Action? setRequestHeaders = null) +#pragma warning restore AV1553 // Do not use optional parameters with default value null for strings, collections or tasks { - return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); + MediaTypeHeaderValue mediaType = contentType == null ? DefaultMediaType : MediaTypeHeaderValue.Parse(contentType); + + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, mediaType, setRequestHeaders); } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePostAtomicAsync(string requestUrl, - object requestBody, string contentType = HeaderConstants.AtomicOperationsMediaType, Action? setRequestHeaders = null) + object requestBody) { - return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, contentType, setRequestHeaders); + Action setRequestHeaders = headers => headers.Accept.Add(OperationsMediaType); + + return await ExecuteRequestAsync(HttpMethod.Post, requestUrl, requestBody, OperationsMediaType, setRequestHeaders); } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecutePatchAsync(string requestUrl, - object requestBody, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) + object requestBody, Action? setRequestHeaders = null) { - return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Patch, requestUrl, requestBody, DefaultMediaType, setRequestHeaders); } public async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteDeleteAsync(string requestUrl, - object? requestBody = null, string contentType = HeaderConstants.MediaType, Action? setRequestHeaders = null) + object? requestBody = null, Action? setRequestHeaders = null) { - return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, contentType, setRequestHeaders); + return await ExecuteRequestAsync(HttpMethod.Delete, requestUrl, requestBody, DefaultMediaType, setRequestHeaders); } private async Task<(HttpResponseMessage httpResponse, TResponseDocument responseDocument)> ExecuteRequestAsync(HttpMethod method, - string requestUrl, object? requestBody, string? contentType, Action? setRequestHeaders) + string requestUrl, object? requestBody, MediaTypeHeaderValue? contentType, Action? setRequestHeaders) { using var request = new HttpRequestMessage(method, requestUrl); string? requestText = SerializeRequest(requestBody); @@ -66,7 +80,7 @@ static IntegrationTest() if (contentType != null) { - request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); + request.Content.Headers.ContentType = contentType; } } diff --git a/test/TestBuildingBlocks/IntegrationTestContext.cs b/test/TestBuildingBlocks/IntegrationTestContext.cs index f479821..b16cd2f 100644 --- a/test/TestBuildingBlocks/IntegrationTestContext.cs +++ b/test/TestBuildingBlocks/IntegrationTestContext.cs @@ -115,9 +115,7 @@ private WebApplicationFactory CreateFactory() services.AddJsonApiMongoDb(); }); - // We have placed an appsettings.json in the TestBuildingBlock project folder and set the content root to there. Note that controllers - // are not discovered in the content root but are registered manually using IntegrationTestContext.UseController. - return factory.WithWebHostBuilder(builder => builder.UseSolutionRelativeContentRoot($"test/{nameof(TestBuildingBlocks)}")); + return factory; } private void ConfigureJsonApiOptions(JsonApiOptions options) @@ -129,6 +127,11 @@ private void ConfigureJsonApiOptions(JsonApiOptions options) public void ConfigureServices(Action configureServices) { + if (_configureServices != null && _configureServices != configureServices) + { + throw new InvalidOperationException($"Do not call {nameof(ConfigureServices)} multiple times."); + } + _configureServices = configureServices; } @@ -169,6 +172,13 @@ public void ConfigureServices(Action? configureServices) _configureServices = configureServices; } + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + // We have placed an appsettings.json in the TestBuildingBlocks project directory and set the content root to there. Note that + // controllers are not discovered in the content root, but are registered manually using IntegrationTestContext.UseController. + builder.UseSolutionRelativeContentRoot($"test/{nameof(TestBuildingBlocks)}"); + } + protected override IHostBuilder CreateHostBuilder() { // @formatter:wrap_chained_method_calls chop_always diff --git a/test/TestBuildingBlocks/MarkedText.cs b/test/TestBuildingBlocks/MarkedText.cs new file mode 100644 index 0000000..003fd0a --- /dev/null +++ b/test/TestBuildingBlocks/MarkedText.cs @@ -0,0 +1,44 @@ +using System.Diagnostics; +using JetBrains.Annotations; + +namespace TestBuildingBlocks; + +[PublicAPI] +[DebuggerDisplay($"{{{nameof(Source)}}}")] +public sealed class MarkedText +{ + public string Source { get; } + public int Position { get; } + public string Text { get; } + + public MarkedText(string source, char marker) + { + ArgumentNullException.ThrowIfNull(source); + + Source = source; + Position = GetPositionFromMarker(marker); + Text = source.Replace(marker.ToString(), string.Empty); + } + + private int GetPositionFromMarker(char marker) + { + int position = Source.IndexOf(marker); + + if (position == -1) + { + throw new InvalidOperationException("Marker not found."); + } + + if (Source.IndexOf(marker, position + 1) != -1) + { + throw new InvalidOperationException("Multiple markers found."); + } + + return position; + } + + public override string ToString() + { + return $"Failed at position {Position + 1}: {Source}"; + } +} diff --git a/test/TestBuildingBlocks/MongoDbSetShim.cs b/test/TestBuildingBlocks/MongoDbSetShim.cs index 182ac6e..8a2801f 100644 --- a/test/TestBuildingBlocks/MongoDbSetShim.cs +++ b/test/TestBuildingBlocks/MongoDbSetShim.cs @@ -42,7 +42,7 @@ public void AddRange(IEnumerable entities) internal override async Task PersistAsync(CancellationToken cancellationToken) { - if (_entitiesToInsert.Any()) + if (_entitiesToInsert.Count > 0) { if (_entitiesToInsert.Count == 1) { diff --git a/test/TestBuildingBlocks/MongoRunnerProvider.cs b/test/TestBuildingBlocks/MongoRunnerProvider.cs index 96af01f..eb0fe49 100644 --- a/test/TestBuildingBlocks/MongoRunnerProvider.cs +++ b/test/TestBuildingBlocks/MongoRunnerProvider.cs @@ -7,7 +7,12 @@ internal sealed class MongoRunnerProvider { public static readonly MongoRunnerProvider Instance = new(); +#if NET8_0 private readonly object _lockObject = new(); +#else + private readonly Lock _lockObject = new(); +#endif + private IMongoRunner? _runner; private int _useCounter; @@ -62,20 +67,14 @@ private sealed class MongoRunnerWrapper(MongoRunnerProvider owner, IMongoRunner public void Import(string database, string collection, string inputFilePath, string? additionalArguments = null, bool drop = false) { - if (_underlyingMongoRunner == null) - { - throw new ObjectDisposedException(nameof(IMongoRunner)); - } + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, this); _underlyingMongoRunner.Import(database, collection, inputFilePath, additionalArguments, drop); } public void Export(string database, string collection, string outputFilePath, string? additionalArguments = null) { - if (_underlyingMongoRunner == null) - { - throw new ObjectDisposedException(nameof(IMongoRunner)); - } + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, this); _underlyingMongoRunner.Export(database, collection, outputFilePath, additionalArguments); } diff --git a/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs b/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs deleted file mode 100644 index a4242b4..0000000 --- a/test/TestBuildingBlocks/NullabilityAssertionExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -using FluentAssertions; -using JetBrains.Annotations; -using SysNotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute; - -// ReSharper disable PossibleMultipleEnumeration -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - -namespace TestBuildingBlocks; - -public static class NullabilityAssertionExtensions -{ - [CustomAssertion] - public static T ShouldNotBeNull([SysNotNull] this T? subject) - { - subject.Should().NotBeNull(); - return subject!; - } - - [CustomAssertion] - public static void ShouldNotBeEmpty([SysNotNull] this string? subject) - { - subject.Should().NotBeEmpty(); - } - - [CustomAssertion] - public static void ShouldHaveCount([SysNotNull] this IEnumerable? subject, int expected) - { - subject.Should().HaveCount(expected); - } - - [CustomAssertion] - public static TValue? ShouldContainKey([SysNotNull] this IDictionary? subject, TKey expected) - { - subject.Should().ContainKey(expected); - - return subject![expected]; - } - - public static void With(this T subject, [InstantHandle] Action continuation) - { - continuation(subject); - } -} diff --git a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs b/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs deleted file mode 100644 index 1e833be..0000000 --- a/test/TestBuildingBlocks/ObjectAssertionsExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Text.Json; -using FluentAssertions; -using FluentAssertions.Collections; -using FluentAssertions.Numeric; -using JetBrains.Annotations; - -namespace TestBuildingBlocks; - -[PublicAPI] -public static class ObjectAssertionsExtensions -{ - private const decimal NumericPrecision = 0.00000000001M; - - /// - /// Same as , but with - /// default precision. - /// - [CustomAssertion] - public static AndConstraint> BeApproximately(this NullableNumericAssertions parent, decimal? expectedValue, - string because = "", params object[] becauseArgs) - { - return parent.BeApproximately(expectedValue, NumericPrecision, because, becauseArgs); - } - - /// - /// Asserts that a "meta" dictionary contains a single element named "total" with the specified value. - /// - [CustomAssertion] - public static void ContainTotal(this GenericDictionaryAssertions, string, object?> source, int expectedTotal) - { - source.ContainKey("total").WhoseValue.Should().BeOfType().Subject.GetInt32().Should().Be(expectedTotal); - } -} diff --git a/test/TestBuildingBlocks/AssemblyInfo.cs b/test/TestBuildingBlocks/Properties/AssemblyInfo.cs similarity index 100% rename from test/TestBuildingBlocks/AssemblyInfo.cs rename to test/TestBuildingBlocks/Properties/AssemblyInfo.cs diff --git a/test/TestBuildingBlocks/ResourceTypeFinder.cs b/test/TestBuildingBlocks/ResourceTypeFinder.cs index c4d6ecf..a1db4f1 100644 --- a/test/TestBuildingBlocks/ResourceTypeFinder.cs +++ b/test/TestBuildingBlocks/ResourceTypeFinder.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Reflection; +using JsonApiDotNetCore.MongoDb; using JsonApiDotNetCore.Resources; #pragma warning disable AV1008 // Class should not be static @@ -16,16 +17,16 @@ public static IReadOnlySet GetResourceClrTypesInNamespace(Assembly assembl IReadOnlySet resourceClrTypesInAssembly = ResourceTypesPerAssembly.GetOrAdd(assembly, GetResourceClrTypesInAssembly); string namespaceKey = codeNamespace ?? string.Empty; - return ResourceTypesPerNamespace.GetOrAdd(namespaceKey, _ => FilterTypesInNamespace(resourceClrTypesInAssembly, codeNamespace).ToHashSet()); + return ResourceTypesPerNamespace.GetOrAdd(namespaceKey, _ => FilterTypesInNamespace(resourceClrTypesInAssembly, codeNamespace)); } private static IReadOnlySet GetResourceClrTypesInAssembly(Assembly assembly) { - return assembly.GetTypes().Where(type => type.IsAssignableTo(typeof(IIdentifiable))).ToHashSet(); + return assembly.GetTypes().Where(type => type.IsAssignableTo(typeof(IIdentifiable))).ToHashSet().AsReadOnly(); } - private static IEnumerable FilterTypesInNamespace(IEnumerable resourceClrTypesInAssembly, string? codeNamespace) + private static IReadOnlySet FilterTypesInNamespace(IEnumerable resourceClrTypesInAssembly, string? codeNamespace) { - return resourceClrTypesInAssembly.Where(resourceClrType => resourceClrType.Namespace == codeNamespace); + return resourceClrTypesInAssembly.Where(resourceClrType => resourceClrType.Namespace == codeNamespace).ToHashSet().AsReadOnly(); } } diff --git a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs index 7ee211a..886a5f0 100644 --- a/test/TestBuildingBlocks/ServiceCollectionExtensions.cs +++ b/test/TestBuildingBlocks/ServiceCollectionExtensions.cs @@ -9,6 +9,9 @@ internal static class ServiceCollectionExtensions { public static void ReplaceControllers(this IServiceCollection services, TestControllerProvider provider) { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(provider); + services.AddMvcCore().ConfigureApplicationPartManager(manager => { RemoveExistingControllerFeatureProviders(manager); diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index d7b0934..060d021 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -1,6 +1,6 @@  - net8.0;net6.0 + net9.0;net8.0 @@ -22,6 +22,6 @@ - + diff --git a/test/TestBuildingBlocks/TestControllerProvider.cs b/test/TestBuildingBlocks/TestControllerProvider.cs index 664fe34..4f183e5 100644 --- a/test/TestBuildingBlocks/TestControllerProvider.cs +++ b/test/TestBuildingBlocks/TestControllerProvider.cs @@ -5,9 +5,9 @@ namespace TestBuildingBlocks; internal sealed class TestControllerProvider : ControllerFeatureProvider { - private readonly ISet _allowedControllerTypes = new HashSet(); + private readonly HashSet _allowedControllerTypes = []; - internal ISet ControllerAssemblies { get; } = new HashSet(); + internal HashSet ControllerAssemblies { get; } = []; public void AddController(Type controller) { diff --git a/tests.runsettings b/tests.runsettings index db83eb9..1f4ca55 100644 --- a/tests.runsettings +++ b/tests.runsettings @@ -1,5 +1,8 @@ + + true + true @@ -7,7 +10,8 @@ - ObsoleteAttribute,GeneratedCodeAttribute + **/test/**/*.* + ObsoleteAttribute,GeneratedCodeAttribute,TestSDKAutoGeneratedCode true From b969800a7d7b2c187d1afdae38503a24be9ffe69 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 27 Apr 2025 01:15:47 +0200 Subject: [PATCH 41/45] Update to latest version of MongoDB.Driver --- package-versions.props | 6 +-- .../Repositories/MongoRepository.cs | 8 ++-- .../TestBuildingBlocks/MongoRunnerProvider.cs | 47 +++++++++++++++---- .../TestBuildingBlocks.csproj | 5 +- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/package-versions.props b/package-versions.props index 14ed6fe..785cca5 100644 --- a/package-versions.props +++ b/package-versions.props @@ -2,16 +2,16 @@ 5.6.0 - 2.28.0 + 3.3.0 35.6.* 6.0.* - 2.0.* + 3.0.* 7.2.* 2.4.* 2.0.* - 2.28.* + 3.3.* 17.13.* 2.9.* 2.8.* diff --git a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs index 0221b00..3a0edfc 100644 --- a/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs +++ b/src/JsonApiDotNetCore.MongoDb/Repositories/MongoRepository.cs @@ -74,7 +74,7 @@ public virtual async Task> GetAsync(QueryLayer qu { ArgumentNullException.ThrowIfNull(queryLayer); - IMongoQueryable query = ApplyQueryLayer(queryLayer); + IQueryable query = ApplyQueryLayer(queryLayer); List? resources = await query.ToListAsync(cancellationToken); return resources.AsReadOnly(); } @@ -89,12 +89,12 @@ public virtual Task CountAsync(FilterExpression? topFilter, CancellationTok Filter = topFilter }; - IMongoQueryable query = ApplyQueryLayer(layer); + IQueryable query = ApplyQueryLayer(layer); return query.CountAsync(cancellationToken); } #pragma warning disable AV1130 // Return type in method signature should be an interface to an unchangeable collection - protected virtual IMongoQueryable ApplyQueryLayer(QueryLayer queryLayer) + protected virtual IQueryable ApplyQueryLayer(QueryLayer queryLayer) #pragma warning restore AV1130 // Return type in method signature should be an interface to an unchangeable collection { ArgumentNullException.ThrowIfNull(queryLayer); @@ -127,7 +127,7 @@ protected virtual IMongoQueryable ApplyQueryLayer(QueryLayer queryLay var context = QueryableBuilderContext.CreateRoot(source, typeof(Queryable), _mongoDataAccess.EntityModel, null); Expression expression = _queryableBuilder.ApplyQuery(queryLayer, context); - return (IMongoQueryable)source.Provider.CreateQuery(expression); + return source.Provider.CreateQuery(expression); } protected virtual IQueryable GetAll() diff --git a/test/TestBuildingBlocks/MongoRunnerProvider.cs b/test/TestBuildingBlocks/MongoRunnerProvider.cs index eb0fe49..6339deb 100644 --- a/test/TestBuildingBlocks/MongoRunnerProvider.cs +++ b/test/TestBuildingBlocks/MongoRunnerProvider.cs @@ -1,4 +1,7 @@ using EphemeralMongo; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; namespace TestBuildingBlocks; @@ -6,6 +9,7 @@ namespace TestBuildingBlocks; internal sealed class MongoRunnerProvider { public static readonly MongoRunnerProvider Instance = new(); + private static readonly GuidSerializer StandardGuidSerializer = new(GuidRepresentation.Standard); #if NET8_0 private readonly object _lockObject = new(); @@ -26,11 +30,13 @@ public IMongoRunner Get() { if (_runner == null) { + BsonSerializer.TryRegisterSerializer(StandardGuidSerializer); + var runnerOptions = new MongoRunnerOptions { // Single-node replica set mode is required for transaction support in MongoDB. UseSingleNodeReplicaSet = true, - AdditionalArguments = "--quiet" + AdditionalArguments = ["--quiet"] }; _runner = MongoRunner.Run(runnerOptions); @@ -63,20 +69,45 @@ private sealed class MongoRunnerWrapper(MongoRunnerProvider owner, IMongoRunner private readonly MongoRunnerProvider _owner = owner; private IMongoRunner? _underlyingMongoRunner = underlyingMongoRunner; - public string ConnectionString => _underlyingMongoRunner?.ConnectionString ?? throw new ObjectDisposedException(nameof(IMongoRunner)); + public string ConnectionString + { + get + { + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, typeof(IMongoRunner)); + return _underlyingMongoRunner.ConnectionString; + } + } + + public void Import(string database, string collection, string inputFilePath, string[]? additionalArguments = null, bool drop = false, + CancellationToken cancellationToken = default) + { + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, typeof(IMongoRunner)); + + _underlyingMongoRunner.Import(database, collection, inputFilePath, additionalArguments, drop, cancellationToken); + } + + public async Task ImportAsync(string database, string collection, string inputFilePath, string[]? additionalArguments = null, bool drop = false, + CancellationToken cancellationToken = default) + { + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, typeof(IMongoRunner)); + + await _underlyingMongoRunner.ImportAsync(database, collection, inputFilePath, additionalArguments, drop, cancellationToken); + } - public void Import(string database, string collection, string inputFilePath, string? additionalArguments = null, bool drop = false) + public void Export(string database, string collection, string outputFilePath, string[]? additionalArguments = null, + CancellationToken cancellationToken = default) { - ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, this); + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, typeof(IMongoRunner)); - _underlyingMongoRunner.Import(database, collection, inputFilePath, additionalArguments, drop); + _underlyingMongoRunner.Export(database, collection, outputFilePath, additionalArguments, cancellationToken); } - public void Export(string database, string collection, string outputFilePath, string? additionalArguments = null) + public async Task ExportAsync(string database, string collection, string outputFilePath, string[]? additionalArguments = null, + CancellationToken cancellationToken = default) { - ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, this); + ObjectDisposedException.ThrowIf(_underlyingMongoRunner == null, typeof(IMongoRunner)); - _underlyingMongoRunner.Export(database, collection, outputFilePath, additionalArguments); + await _underlyingMongoRunner.ExportAsync(database, collection, outputFilePath, additionalArguments, cancellationToken); } public void Dispose() diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 060d021..79c9947 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -12,10 +12,7 @@ - - - - + From 8d465fdce7bcb67af1fce1e801c31a462e10efc1 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 27 Apr 2025 01:19:49 +0200 Subject: [PATCH 42/45] Update to JsonApiDotNetCore 5.7.1 --- package-versions.props | 2 +- test/TestBuildingBlocks/IntegrationTest.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-versions.props b/package-versions.props index 785cca5..9bf2a49 100644 --- a/package-versions.props +++ b/package-versions.props @@ -1,7 +1,7 @@ - 5.6.0 + 5.7.1 3.3.0 diff --git a/test/TestBuildingBlocks/IntegrationTest.cs b/test/TestBuildingBlocks/IntegrationTest.cs index e74c892..4fc245a 100644 --- a/test/TestBuildingBlocks/IntegrationTest.cs +++ b/test/TestBuildingBlocks/IntegrationTest.cs @@ -13,10 +13,10 @@ namespace TestBuildingBlocks; /// public abstract class IntegrationTest : IAsyncLifetime { - private static readonly MediaTypeHeaderValue DefaultMediaType = MediaTypeHeaderValue.Parse(HeaderConstants.MediaType); + private static readonly MediaTypeHeaderValue DefaultMediaType = MediaTypeHeaderValue.Parse(JsonApiMediaType.Default.ToString()); private static readonly MediaTypeWithQualityHeaderValue OperationsMediaType = - MediaTypeWithQualityHeaderValue.Parse(HeaderConstants.AtomicOperationsMediaType); + MediaTypeWithQualityHeaderValue.Parse(JsonApiMediaType.AtomicOperations.ToString()); private static readonly SemaphoreSlim ThrottleSemaphore = GetDefaultThrottleSemaphore(); From c47b980b08d79514506d95fe047b0965149c631c Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 27 Apr 2025 03:41:56 +0200 Subject: [PATCH 43/45] Increment version to 5.7.2 (used for pre-release builds from ci) --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 86f7636..9619270 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,7 +9,7 @@ Recommended $(MSBuildThisFileDirectory)CodingGuidelines.ruleset $(MSBuildThisFileDirectory)tests.runsettings - 5.7.1 + 5.7.2 pre direct From 663f215e041114b70885772189e11e25dd4433f4 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Sun, 27 Apr 2025 03:44:46 +0200 Subject: [PATCH 44/45] Cleanup README.md --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7a0e52d..b7f6f9c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ The following steps describe how to create a JSON:API project with MongoDB. 1. Configure MongoDB and JsonApiDotNetCore in `Program.cs`, seeding the database with sample data: ```c# var builder = WebApplication.CreateBuilder(args); - builder.Services.AddSingleton(_ => new MongoClient("mongodb://localhost:27017").GetDatabase("ExampleDbName")); + builder.Services.AddSingleton(_ => + new MongoClient("mongodb://localhost:27017").GetDatabase("ExampleDbName")); builder.Services.AddJsonApi(options => { options.UseRelativeLinks = true; @@ -74,19 +75,6 @@ The following steps describe how to create a JSON:API project with MongoDB. } ``` - > [!TIP] - > If your API project uses MongoDB only (so not in combination with EF Core), then instead of - > registering all MongoDB resources and repositories individually, you can use: - > - > ```c# - > builder.Services.AddJsonApi(facade => facade.AddCurrentAssembly()); - > builder.Services.AddJsonApiMongoDb(); - > - > builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); - > builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); - > builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); - > ``` - 1. Start your API ```bash dotnet run @@ -137,6 +125,19 @@ The following steps describe how to create a JSON:API project with MongoDB. +> [!TIP] +> If your API project uses MongoDB only (so not in combination with EF Core), then instead of +> registering all MongoDB resources and repositories individually, you can use: +> +> ```c# +> builder.Services.AddJsonApi(facade => facade.AddCurrentAssembly()); +> builder.Services.AddJsonApiMongoDb(); +> +> builder.Services.AddScoped(typeof(IResourceReadRepository<,>), typeof(MongoRepository<,>)); +> builder.Services.AddScoped(typeof(IResourceWriteRepository<,>), typeof(MongoRepository<,>)); +> builder.Services.AddScoped(typeof(IResourceRepository<,>), typeof(MongoRepository<,>)); +> ``` + ## Using client-generated IDs Resources that inherit from `HexStringMongoIdentifiable` use auto-generated (high-performance) 12-byte hexadecimal From 48e257c4b915304512ec6c692107329e4ca4d3a2 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:53:00 +0200 Subject: [PATCH 45/45] Update EphemeralMongo to fix flaky tests with "Text file busy" on Ubuntu (#97) --- package-versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-versions.props b/package-versions.props index 9bf2a49..94750ca 100644 --- a/package-versions.props +++ b/package-versions.props @@ -7,7 +7,7 @@ 35.6.* 6.0.* - 3.0.* + 3.1.* 7.2.* 2.4.* 2.0.*