diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9f4b9f1d56..de5a0b2ff0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2025.2.4", + "version": "2025.3.0.2", "commands": [ "jb" ], @@ -17,14 +17,14 @@ "rollForward": false }, "dotnet-reportgenerator-globaltool": { - "version": "5.4.17", + "version": "5.5.0", "commands": [ "reportgenerator" ], "rollForward": false }, "docfx": { - "version": "2.78.2", + "version": "2.78.4", "commands": [ "docfx" ], diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35313888ef..3e9f8edd9c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,6 +48,7 @@ jobs: dotnet-version: | 8.0.* 9.0.* + 10.0.* - name: Show installed versions shell: pwsh run: | @@ -159,6 +160,7 @@ jobs: dotnet-version: | 8.0.* 9.0.* + 10.0.* - name: Git checkout uses: actions/checkout@v5 - name: Restore tools @@ -168,6 +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 --version dotnet jb inspectcode JsonApiDotNetCore.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: Upload output to artifacts uses: actions/upload-artifact@v4 @@ -218,6 +221,7 @@ jobs: dotnet-version: | 8.0.* 9.0.* + 10.0.* - name: Git checkout uses: actions/checkout@v5 with: @@ -236,12 +240,14 @@ jobs: $baseCommitHash = git rev-parse HEAD~1 Write-Output "Running code cleanup on commit range $baseCommitHash..$headCommitHash in pull request." + dotnet jb cleanupcode --version dotnet regitlint -s JsonApiDotNetCore.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' || github.event_name == 'workflow_dispatch' }} shell: pwsh run: | Write-Output 'Running code cleanup on all files.' + dotnet jb cleanupcode --version dotnet regitlint -s JsonApiDotNetCore.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: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0ff1bb17be..35c8171615 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,6 +28,7 @@ jobs: dotnet-version: | 8.0.* 9.0.* + 10.0.* - name: Git checkout uses: actions/checkout@v5 - name: Initialize CodeQL diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml index 6a91aebccc..83405f8027 100644 --- a/.github/workflows/qodana.yml +++ b/.github/workflows/qodana.yml @@ -27,6 +27,7 @@ jobs: dotnet-version: | 8.0.* 9.0.* + 10.0.* - name: Git checkout uses: actions/checkout@v5 with: diff --git a/.gitignore b/.gitignore index c1757fc159..85bd0f1080 100644 --- a/.gitignore +++ b/.gitignore @@ -423,6 +423,3 @@ FodyWeavers.xsd **/.idea/**/httpRequests/ **/.idea/**/dataSources/ !**/.idea/**/codeStyles/* - -# Workaround for https://github.com/microsoft/kiota/issues/4228 -kiota-lock.json diff --git a/Directory.Build.props b/Directory.Build.props index e5323c6c76..46088e01b0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,17 @@ 5.9.2 pre 7 - direct + + + + + + $(NoWarn);NU1608 + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000000..61e668c6b9 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings index 3576ab2bd8..e340302b69 100644 --- a/JsonApiDotNetCore.sln.DotSettings +++ b/JsonApiDotNetCore.sln.DotSettings @@ -72,8 +72,10 @@ WARNING SUGGESTION SUGGESTION + WARNING SUGGESTION WARNING + WARNING DO_NOT_SHOW WARNING WARNING diff --git a/README.md b/README.md index d9719db341..b15b2dc5cc 100644 --- a/README.md +++ b/README.md @@ -219,23 +219,17 @@ The following links explain what this project provides, why it exists, and how y The following chart should help you pick the best version, based on your environment. See also our [versioning policy](./VERSIONING_POLICY.md). -| JsonApiDotNetCore | Status | .NET | Entity Framework Core | -| ----------------- | ------------ | -------- | --------------------- | -| master | Preview | 9 | 9 | -| | | 8 | 8, 9 | -| 5.7.0+ | Stable | 9 | 9 | -| | | 8 | 8, 9 | -| 5.5.0-5.6.0 | Stable | 9 | 9 | -| | | 8 | 8, 9 | -| | | 7 | 7 | -| | | 6 | 6, 7 | -| 5.0.3-5.4.0 | Stable | 7 | 7 | -| | | 6 | 6, 7 | -| 5.0.0-5.0.2 | Stable | 6 | 6 | -| 4.x | Stable | 6 | 5 | -| | | 5 | 5 | -| | | Core 3.1 | 3.1, 5 | -| 3.x | Stable | Core 2.x | 2.x | +| .NET | Entity Framework Core | JsonApiDotNetCore | Status | +| -------- | --------------------- | ----------------- | -------------- | +| 10 | 10 | master | Preview | +| 9 | 9 | 5.5.0+ | Stable | +| 8 | 8, 9 | 5.5.0+ | Stable | +| 7 | 7 | 5.0.3 - 5.6.0 | Out of support | +| 6 | 7 | 5.0.3 - 5.6.0 | Out of support | +| 6 | 6 | 5.0.0 - 5.6.0 | Out of support | +| 5 | 5 | 4.x | Out of support | +| Core 3.1 | 3.1, 5 | 4.x | Out of support | +| Core 2.x | 2.x | 3.x | Out of support | ## Trying out the latest build diff --git a/benchmarks/Benchmarks.csproj b/benchmarks/Benchmarks.csproj index 90d53461d2..6bd78a5cdd 100644 --- a/benchmarks/Benchmarks.csproj +++ b/benchmarks/Benchmarks.csproj @@ -1,7 +1,7 @@ Exe - net9.0 + net10.0 true diff --git a/cleanupcode.ps1 b/cleanupcode.ps1 index b35d1cb215..d87c58a342 100644 --- a/cleanupcode.ps1 +++ b/cleanupcode.ps1 @@ -28,17 +28,20 @@ if ($revision) { if ($baseCommitHash -eq $headCommitHash) { Write-Output "Running code cleanup on staged/unstaged files." + dotnet jb cleanupcode --version dotnet regitlint -s JsonApiDotNetCore.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 jb cleanupcode --version dotnet regitlint -s JsonApiDotNetCore.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 jb cleanupcode --version dotnet regitlint -s JsonApiDotNetCore.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/docs/generate-examples.ps1 b/docs/generate-examples.ps1 index ea6b2bd8f2..dced9ce131 100644 --- a/docs/generate-examples.ps1 +++ b/docs/generate-examples.ps1 @@ -34,7 +34,7 @@ function Start-WebServer { Write-Output "Starting web server" $startTimeUtc = Get-Date -AsUTC $job = Start-Job -ScriptBlock { - dotnet run --project ..\src\Examples\GettingStarted\GettingStarted.csproj --framework net8.0 --configuration Debug --property:TreatWarningsAsErrors=True --urls=http://0.0.0.0:14141 + dotnet run --project ../src/Examples/GettingStarted/GettingStarted.csproj --framework net10.0 --configuration Debug --property:TreatWarningsAsErrors=True --urls=http://0.0.0.0:14141 } $webProcessId = $null diff --git a/inspectcode.ps1 b/inspectcode.ps1 index 21e96eac67..e3ae50660b 100644 --- a/inspectcode.ps1 +++ b/inspectcode.ps1 @@ -10,6 +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 --version dotnet jb inspectcode JsonApiDotNetCore.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) { diff --git a/package-versions.props b/package-versions.props index a4fa94ce08..eb57740bd9 100644 --- a/package-versions.props +++ b/package-versions.props @@ -5,7 +5,7 @@ 0.4.1 2.14.1 13.0.4 - 9.0.6 + 10.0.1 4.3.1 @@ -13,28 +13,40 @@ 0.15.* 1.0.* 35.6.* - 4.14.* + 5.0.* 6.0.* 2.1.* 7.2.* 2.4.* 2.0.* 1.* - 9.0.* - 9.0.* + 10.0.* 0.9.* 14.6.* 13.0.* - 8.9.* + 9.1.* 4.1.* - 2.9.* - 9.*-* - 9.0.* + 2.11.* + 10.*-* + 10.0.* 18.0.* 2.9.* 3.1.* + + + 10.0.0 + + + 10.0.* + 10.0.*-* + + + 9.0.* + + + N/A @@ -42,6 +54,7 @@ 9.0.* 9.0.* + $(EntityFrameworkCoreVersion) @@ -51,5 +64,6 @@ 8.0.* 8.0.* + $(EntityFrameworkCoreVersion) diff --git a/src/Examples/DapperExample/DapperExample.csproj b/src/Examples/DapperExample/DapperExample.csproj index 0685dc0343..f34631b4a8 100644 --- a/src/Examples/DapperExample/DapperExample.csproj +++ b/src/Examples/DapperExample/DapperExample.csproj @@ -1,6 +1,6 @@ - + - net9.0;net8.0 + net10.0;net9.0;net8.0 @@ -13,9 +13,9 @@ - + - + diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableSourceNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableSourceNode.cs index 62ff5ad3ef..4b0fa2d5b5 100644 --- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableSourceNode.cs +++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableSourceNode.cs @@ -7,7 +7,7 @@ namespace DapperExample.TranslationToSql.TreeNodes; /// internal abstract class TableSourceNode(string? alias) : SqlTreeNode { - public const string IdColumnName = nameof(Identifiable.Id); + public const string IdColumnName = nameof(Identifiable<>.Id); public abstract IReadOnlyList Columns { get; } public string? Alias { get; } = alias; diff --git a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj index 3edc993428..c7cee804b1 100644 --- a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj +++ b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj index 611aeb37a5..806fdf45a5 100644 --- a/src/Examples/GettingStarted/GettingStarted.csproj +++ b/src/Examples/GettingStarted/GettingStarted.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json index 8b23ba14e3..f32c5983c0 100644 --- a/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json +++ b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json @@ -344,13 +344,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -358,6 +351,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -631,13 +631,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -645,6 +638,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -947,13 +947,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -961,6 +954,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1125,13 +1125,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1139,6 +1132,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1543,13 +1543,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1557,6 +1550,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1721,13 +1721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1735,6 +1728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2107,13 +2107,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2121,6 +2114,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2394,13 +2394,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2408,6 +2401,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2710,13 +2710,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2724,6 +2717,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2888,13 +2888,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2902,6 +2895,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3274,13 +3274,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3288,6 +3281,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3561,13 +3561,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3575,6 +3568,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3877,13 +3877,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3891,6 +3884,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4055,13 +4055,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4069,6 +4062,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4313,13 +4313,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4327,6 +4320,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4491,13 +4491,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4505,6 +4498,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4749,13 +4749,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4763,6 +4756,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4927,13 +4927,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4941,6 +4934,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8716,5 +8716,22 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "nonJsonApi" + }, + { + "name": "operations" + }, + { + "name": "people" + }, + { + "name": "tags" + }, + { + "name": "todoItems" + } + ] } \ No newline at end of file diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj index 768a2de827..58feac6a6d 100644 --- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj +++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 true GeneratedSwagger diff --git a/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs b/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs index 894c0d0966..f1a162698f 100644 --- a/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs +++ b/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs @@ -1,5 +1,5 @@ using JetBrains.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCoreExample; @@ -16,6 +16,8 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { if (_httpContextAccessor.HttpContext == null) { + swaggerDoc.Servers ??= new List(); + swaggerDoc.Servers.Add(new OpenApiServer { Url = "https://localhost:44340" diff --git a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj index 611aeb37a5..806fdf45a5 100644 --- a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj +++ b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj index 15a485c08f..e50874f733 100644 --- a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj +++ b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/kiota-lock.json b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..8679b89855 --- /dev/null +++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "19BB642CD5A9FBA03BAFE3C25B95140A5EC2082F079AA92E8814025B74EB0E70AF07CBF99482F06848C77C32049C0F3C8BFB2B25E2519BAE97F8DEC313313960", + "descriptionLocation": "../../JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "ExampleApiClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaClientExample.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/src/Examples/OpenApiKiotaClientExample/OpenApiKiotaClientExample.csproj b/src/Examples/OpenApiKiotaClientExample/OpenApiKiotaClientExample.csproj index 8f65b2b688..8dd3e2e4df 100644 --- a/src/Examples/OpenApiKiotaClientExample/OpenApiKiotaClientExample.csproj +++ b/src/Examples/OpenApiKiotaClientExample/OpenApiKiotaClientExample.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 diff --git a/src/Examples/OpenApiNSwagClientExample/.editorconfig b/src/Examples/OpenApiNSwagClientExample/.editorconfig deleted file mode 100644 index e2ec1cac44..0000000000 --- a/src/Examples/OpenApiNSwagClientExample/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -# Workaround for incorrect nullability in NSwag generated clients. -[*Client.cs] -dotnet_diagnostic.CS8765.severity = none diff --git a/src/Examples/OpenApiNSwagClientExample/OpenApiNSwagClientExample.csproj b/src/Examples/OpenApiNSwagClientExample/OpenApiNSwagClientExample.csproj index c30833a39a..5523c5eba3 100644 --- a/src/Examples/OpenApiNSwagClientExample/OpenApiNSwagClientExample.csproj +++ b/src/Examples/OpenApiNSwagClientExample/OpenApiNSwagClientExample.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 @@ -12,7 +12,6 @@ - diff --git a/src/Examples/ReportsExample/ReportsExample.csproj b/src/Examples/ReportsExample/ReportsExample.csproj index 6ade1386be..73a16ddf4f 100644 --- a/src/Examples/ReportsExample/ReportsExample.csproj +++ b/src/Examples/ReportsExample/ReportsExample.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj index d615476081..fd646bfd78 100644 --- a/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj +++ b/src/JsonApiDotNetCore.Annotations/JsonApiDotNetCore.Annotations.csproj @@ -1,6 +1,6 @@ - net8.0;netstandard1.0 + net10.0;net8.0;netstandard1.0 true true JsonApiDotNetCore diff --git a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj index 640b949477..fce1fb7c40 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj +++ b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0;net8.0 true true false diff --git a/src/JsonApiDotNetCore.OpenApi.Client.NSwag/JsonApiDotNetCore.OpenApi.Client.NSwag.csproj b/src/JsonApiDotNetCore.OpenApi.Client.NSwag/JsonApiDotNetCore.OpenApi.Client.NSwag.csproj index 20e2306730..550f284509 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client.NSwag/JsonApiDotNetCore.OpenApi.Client.NSwag.csproj +++ b/src/JsonApiDotNetCore.OpenApi.Client.NSwag/JsonApiDotNetCore.OpenApi.Client.NSwag.csproj @@ -1,6 +1,6 @@  - net8.0 + net10.0;net8.0 true true false diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs index c11683b50d..77be1e7ce1 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiActionDescriptorCollectionProvider.cs @@ -17,7 +17,6 @@ using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.Logging; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; @@ -27,7 +26,7 @@ namespace JsonApiDotNetCore.OpenApi.Swashbuckle; /// /article/{id}/{relationshipName} -> /article/{id}/author, /article/{id}/revisions, etc. /// ]]> /// -internal sealed partial class JsonApiActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider +internal sealed class JsonApiActionDescriptorCollectionProvider : IActionDescriptorCollectionProvider { private const int FilterScope = 10; private static readonly Type ErrorDocumentType = typeof(ErrorResponseDocument); @@ -35,24 +34,21 @@ internal sealed partial class JsonApiActionDescriptorCollectionProvider : IActio private readonly IActionDescriptorCollectionProvider _defaultProvider; private readonly IControllerResourceMapping _controllerResourceMapping; private readonly JsonApiEndpointMetadataProvider _jsonApiEndpointMetadataProvider; - private readonly ILogger _logger; private readonly ConcurrentDictionary> _versionedActionDescriptorCache = new(); public ActionDescriptorCollection ActionDescriptors => _versionedActionDescriptorCache.GetOrAdd(_defaultProvider.ActionDescriptors.Version, LazyGetActionDescriptors).Value; public JsonApiActionDescriptorCollectionProvider(IActionDescriptorCollectionProvider defaultProvider, IControllerResourceMapping controllerResourceMapping, - JsonApiEndpointMetadataProvider jsonApiEndpointMetadataProvider, ILogger logger) + JsonApiEndpointMetadataProvider jsonApiEndpointMetadataProvider) { ArgumentNullException.ThrowIfNull(defaultProvider); ArgumentNullException.ThrowIfNull(controllerResourceMapping); ArgumentNullException.ThrowIfNull(jsonApiEndpointMetadataProvider); - ArgumentNullException.ThrowIfNull(logger); _defaultProvider = defaultProvider; _controllerResourceMapping = controllerResourceMapping; _jsonApiEndpointMetadataProvider = jsonApiEndpointMetadataProvider; - _logger = logger; } private Lazy LazyGetActionDescriptors(int version) @@ -478,7 +474,4 @@ private static void SetNonPrimaryResponseMetadata(ActionDescriptor descriptor, descriptorsByRelationship[relationship] = relationshipDescriptor; } - - [LoggerMessage(Level = LogLevel.Warning, Message = "Hiding unsupported custom JSON:API action method [{HttpMethods}] {ActionMethod} in OpenAPI.")] - private partial void LogSuppressedActionMethod(string httpMethods, string? actionMethod); } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj index 4a57ca1c85..a32d89a5ec 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiDotNetCore.OpenApi.Swashbuckle.csproj @@ -1,6 +1,6 @@ - net8.0 + net10.0;net8.0 true true false diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs index 10095834b2..a954af0de9 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/OpenApiSchemaExtensions.cs @@ -1,30 +1,70 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; internal static class OpenApiSchemaExtensions { - public static void ReorderProperties(this OpenApiSchema fullSchema, IEnumerable propertyNamesInOrder) + public static OpenApiSchema AsInlineSchema(this IOpenApiSchema schema) { - ArgumentNullException.ThrowIfNull(fullSchema); - ArgumentNullException.ThrowIfNull(propertyNamesInOrder); + ConsistencyGuard.ThrowIf(schema is not OpenApiSchema); + return (OpenApiSchema)schema; + } - var propertiesInOrder = new Dictionary(); + public static OpenApiSchemaReference AsReferenceSchema(this IOpenApiSchema schema) + { + ConsistencyGuard.ThrowIf(schema is not OpenApiSchemaReference); + return (OpenApiSchemaReference)schema; + } - foreach (string propertyName in propertyNamesInOrder) + public static string GetReferenceId(this OpenApiSchemaReference referenceSchema) + { + string? schemaId = referenceSchema.Reference.Id; + ConsistencyGuard.ThrowIf(schemaId is null); + return schemaId; + } + + public static void SetNullable(this OpenApiSchema inlineSchema, bool nullable) + { + ArgumentNullException.ThrowIfNull(inlineSchema); + + if (nullable) + { + inlineSchema.Type ??= JsonSchemaType.Null; + inlineSchema.Type |= JsonSchemaType.Null; + } + else { - if (fullSchema.Properties.TryGetValue(propertyName, out OpenApiSchema? schema)) + if (inlineSchema.Type != null) { - propertiesInOrder.Add(propertyName, schema); + inlineSchema.Type &= ~JsonSchemaType.Null; } } + } + + public static void ReorderProperties(this OpenApiSchema inlineSchema, IEnumerable propertyNamesInOrder) + { + ArgumentNullException.ThrowIfNull(inlineSchema); + ArgumentNullException.ThrowIfNull(propertyNamesInOrder); + + if (inlineSchema.Properties is { Count: > 1 }) + { + var propertiesInOrder = new Dictionary(); - ConsistencyGuard.ThrowIf(fullSchema.Properties.Count != propertiesInOrder.Count); + foreach (string propertyName in propertyNamesInOrder) + { + if (inlineSchema.Properties.TryGetValue(propertyName, out IOpenApiSchema? schema)) + { + propertiesInOrder.Add(propertyName, schema); + } + } - fullSchema.Properties = propertiesInOrder; + ConsistencyGuard.ThrowIf(inlineSchema.Properties.Count != propertiesInOrder.Count); + + inlineSchema.Properties = propertiesInOrder; + } } - public static OpenApiSchema WrapInExtendedSchema(this OpenApiSchema source) + public static OpenApiSchema WrapInExtendedSchema(this IOpenApiSchema source) { ArgumentNullException.ThrowIfNull(source); @@ -34,11 +74,11 @@ public static OpenApiSchema WrapInExtendedSchema(this OpenApiSchema source) }; } - public static OpenApiSchema UnwrapLastExtendedSchema(this OpenApiSchema source) + public static IOpenApiSchema UnwrapLastExtendedSchema(this IOpenApiSchema source) { ArgumentNullException.ThrowIfNull(source); - if (source.AllOf is { Count: > 0 }) + if (source is OpenApiSchema && source.AllOf is { Count: > 0 }) { return source.AllOf.Last(); } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/AtomicOperationCodeSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/AtomicOperationCodeSchemaGenerator.cs index 78a25da441..c7022c195a 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/AtomicOperationCodeSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/AtomicOperationCodeSchemaGenerator.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -19,7 +18,7 @@ public AtomicOperationCodeSchemaGenerator(SchemaGenerationTracer schemaGeneratio _schemaIdSelector = schemaIdSelector; } - public OpenApiSchema GenerateSchema(AtomicOperationCode operationCode, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(AtomicOperationCode operationCode, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaRepository); @@ -27,27 +26,20 @@ public OpenApiSchema GenerateSchema(AtomicOperationCode operationCode, SchemaRep if (schemaRepository.Schemas.ContainsKey(schemaId)) { - return new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = schemaId, - Type = ReferenceType.Schema - } - }; + return new OpenApiSchemaReference(schemaId); } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, operationCode); string enumValue = operationCode.ToString().ToLowerInvariant(); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "string", - Enum = [new OpenApiString(enumValue)] + Type = JsonSchemaType.String, + Enum = [enumValue] }; - OpenApiSchema referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + OpenApiSchemaReference referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); traceScope.TraceSucceeded(schemaId); return referenceSchema; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs index c4b41dd0f5..3501c1f32f 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataContainerSchemaGenerator.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -27,49 +27,44 @@ public DataContainerSchemaGenerator(SchemaGenerationTracer schemaGenerationTrace _resourceGraph = resourceGraph; } - public OpenApiSchema GenerateSchemaForCommonResourceDataInResponse(SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchemaForCommonResourceDataInResponse(SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaRepository); return _dataSchemaGenerator.GenerateSchemaForCommonData(typeof(ResourceInResponse), schemaRepository); } - public OpenApiSchema GenerateSchema(Type dataContainerSchemaType, ResourceType resourceType, bool forRequestSchema, bool canIncludeRelated, + public void GenerateSchema(Type dataContainerSchemaType, ResourceType resourceType, bool forRequestSchema, bool canIncludeRelated, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(dataContainerSchemaType); ArgumentNullException.ThrowIfNull(resourceType); ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(dataContainerSchemaType, out OpenApiSchema referenceSchemaForData)) + if (!schemaRepository.TryLookupByTypeSafe(dataContainerSchemaType, out _)) { - return referenceSchemaForData; - } - - Type dataConstructedType = GetElementTypeOfDataProperty(dataContainerSchemaType, resourceType); - - if (schemaRepository.TryLookupByType(dataConstructedType, out _)) - { - return referenceSchemaForData; - } + Type dataConstructedType = GetElementTypeOfDataProperty(dataContainerSchemaType, resourceType); - using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, dataConstructedType); - - if (canIncludeRelated) - { - var resourceSchemaType = ResourceSchemaType.Create(dataConstructedType, _resourceGraph); - - if (resourceSchemaType.SchemaOpenType == typeof(DataInResponse<>)) + if (!schemaRepository.TryLookupByTypeSafe(dataConstructedType, out _)) { - // Ensure all reachable related resource types in response schemas are generated upfront. - // This is needed to make includes work when not all endpoints are exposed. - GenerateReachableRelatedTypesInResponse(dataConstructedType, schemaRepository); + using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, dataConstructedType); + + if (canIncludeRelated) + { + var resourceSchemaType = ResourceSchemaType.Create(dataConstructedType, _resourceGraph); + + if (resourceSchemaType.SchemaOpenType == typeof(DataInResponse<>)) + { + // Ensure all reachable related resource types in response schemas are generated upfront. + // This is needed to make includes work when not all endpoints are exposed. + GenerateReachableRelatedTypesInResponse(dataConstructedType, schemaRepository); + } + } + + OpenApiSchemaReference referenceSchemaForData = _dataSchemaGenerator.GenerateSchema(dataConstructedType, forRequestSchema, schemaRepository); + traceScope.TraceSucceeded(referenceSchemaForData.GetReferenceId()); } } - - referenceSchemaForData = _dataSchemaGenerator.GenerateSchema(dataConstructedType, forRequestSchema, schemaRepository); - traceScope.TraceSucceeded(referenceSchemaForData.Reference.Id); - return referenceSchemaForData; } private static Type GetElementTypeOfDataProperty(Type dataContainerConstructedType, ResourceType resourceType) diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs index 4d7783f780..6db68645fc 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/DataSchemaGenerator.cs @@ -1,12 +1,13 @@ using System.Collections.Concurrent; using System.Reflection; using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Nodes; using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -79,7 +80,7 @@ public DataSchemaGenerator(SchemaGenerationTracer schemaGenerationTracer, Schema _resourceDocumentationReader = resourceDocumentationReader; } - public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(Type dataSchemaType, bool forRequestSchema, SchemaRepository schemaRepository) { // For a given resource (identifier) type, we always generate the full type hierarchy. Discriminator mappings // are managed manually, because there's no way to intercept in the Swashbuckle recursive component schema generation. @@ -87,7 +88,7 @@ public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, ArgumentNullException.ThrowIfNull(dataSchemaType); ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(dataSchemaType, out OpenApiSchema referenceSchemaForData)) + if (schemaRepository.TryLookupByTypeSafe(dataSchemaType, out OpenApiSchemaReference? referenceSchemaForData)) { return referenceSchemaForData; } @@ -114,18 +115,18 @@ public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, dataSchemaType); - referenceSchemaForData = _defaultSchemaGenerator.GenerateSchema(dataSchemaType, schemaRepository); - OpenApiSchema fullSchemaForData = schemaRepository.Schemas[referenceSchemaForData.Reference.Id]; - fullSchemaForData.AdditionalPropertiesAllowed = false; + referenceSchemaForData = _defaultSchemaGenerator.GenerateSchema(dataSchemaType, schemaRepository).AsReferenceSchema(); + OpenApiSchema possiblyCompositeInlineSchemaForData = schemaRepository.Schemas[referenceSchemaForData.GetReferenceId()].AsInlineSchema(); + possiblyCompositeInlineSchemaForData.AdditionalPropertiesAllowed = false; - OpenApiSchema inlineSchemaForData = fullSchemaForData.UnwrapLastExtendedSchema(); + OpenApiSchema inlineSchemaForData = possiblyCompositeInlineSchemaForData.UnwrapLastExtendedSchema().AsInlineSchema(); SetAbstract(inlineSchemaForData, resourceSchemaType); SetResourceType(inlineSchemaForData, resourceType, schemaRepository); AdaptResourceIdentity(inlineSchemaForData, resourceSchemaType, forRequestSchema, schemaRepository); SetResourceId(inlineSchemaForData, resourceType, schemaRepository); SetResourceFields(inlineSchemaForData, resourceSchemaType, forRequestSchema, schemaRepository); - SetDocumentation(fullSchemaForData, resourceType); + SetDocumentation(possiblyCompositeInlineSchemaForData, resourceType); SetLinksVisibility(inlineSchemaForData, resourceSchemaType, schemaRepository); if (resourceType.IsPartOfTypeHierarchy()) @@ -142,10 +143,11 @@ public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, if (RequiresRootObjectTypeInDataSchema(resourceSchemaType, forRequestSchema)) { - fullSchemaForData.Extensions[SetSchemaTypeToObjectDocumentFilter.RequiresRootObjectTypeKey] = new OpenApiBoolean(true); + possiblyCompositeInlineSchemaForData.Extensions ??= new SortedDictionary(StringComparer.Ordinal); + possiblyCompositeInlineSchemaForData.Extensions[SetSchemaTypeToObjectDocumentFilter.RequiresRootObjectTypeKey] = new JsonNodeExtension(true); } - traceScope.TraceSucceeded(referenceSchemaForData.Reference.Id); + traceScope.TraceSucceeded(referenceSchemaForData.GetReferenceId()); return referenceSchemaForData; } @@ -202,45 +204,44 @@ public OpenApiSchema GenerateSchema(Type dataSchemaType, bool forRequestSchema, return boxedSchemaType.Value; } - public OpenApiSchema GenerateSchemaForCommonData(Type commonDataSchemaType, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchemaForCommonData(Type commonDataSchemaType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(commonDataSchemaType); ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(commonDataSchemaType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(commonDataSchemaType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, commonDataSchemaType); - OpenApiSchema referenceSchemaForResourceType = _resourceTypeSchemaGenerator.GenerateSchema(schemaRepository); - OpenApiSchema referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); + OpenApiSchemaReference referenceSchemaForResourceType = _resourceTypeSchemaGenerator.GenerateSchema(schemaRepository); + OpenApiSchemaReference referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "object", - Required = new SortedSet([JsonApiPropertyName.Type]), - Properties = new Dictionary + Type = JsonSchemaType.Object, + Required = new SortedSet([JsonApiPropertyName.Type], StringComparer.Ordinal), + Properties = new Dictionary { [JsonApiPropertyName.Type] = referenceSchemaForResourceType.WrapInExtendedSchema(), - [referenceSchemaForMeta.Reference.Id] = referenceSchemaForMeta.WrapInExtendedSchema() + [referenceSchemaForMeta.GetReferenceId()] = referenceSchemaForMeta.WrapInExtendedSchema() }, AdditionalPropertiesAllowed = false, Discriminator = new OpenApiDiscriminator { - PropertyName = JsonApiPropertyName.Type, - Mapping = new SortedDictionary(StringComparer.Ordinal) + PropertyName = JsonApiPropertyName.Type }, - Extensions = + Extensions = new SortedDictionary(StringComparer.Ordinal) { - ["x-abstract"] = new OpenApiBoolean(true) + ["x-abstract"] = new JsonNodeExtension(true) } }; string schemaId = _schemaIdSelector.GetSchemaId(commonDataSchemaType); - referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(commonDataSchemaType, schemaId); traceScope.TraceSucceeded(schemaId); @@ -268,24 +269,25 @@ private static Type ChangeResourceTypeInSchemaType(Type schemaType, ResourceType return schemaOpenType.MakeGenericType(resourceType.ClrType); } - private static void SetAbstract(OpenApiSchema fullSchema, ResourceSchemaType resourceSchemaType) + private static void SetAbstract(OpenApiSchema inlineSchema, ResourceSchemaType resourceSchemaType) { if (resourceSchemaType.ResourceType.ClrType.IsAbstract && resourceSchemaType.SchemaOpenType != typeof(IdentifierInRequest<>)) { - fullSchema.Extensions["x-abstract"] = new OpenApiBoolean(true); + inlineSchema.Extensions ??= new SortedDictionary(StringComparer.Ordinal); + inlineSchema.Extensions["x-abstract"] = new JsonNodeExtension(true); } } - private void SetResourceType(OpenApiSchema fullSchema, ResourceType resourceType, SchemaRepository schemaRepository) + private void SetResourceType(OpenApiSchema inlineSchema, ResourceType resourceType, SchemaRepository schemaRepository) { - if (fullSchema.Properties.ContainsKey(JsonApiPropertyName.Type)) + if (inlineSchema.Properties != null && inlineSchema.Properties.ContainsKey(JsonApiPropertyName.Type)) { - OpenApiSchema referenceSchema = _resourceTypeSchemaGenerator.GenerateSchema(resourceType, schemaRepository); - fullSchema.Properties[JsonApiPropertyName.Type] = referenceSchema.WrapInExtendedSchema(); + OpenApiSchemaReference referenceSchema = _resourceTypeSchemaGenerator.GenerateSchema(resourceType, schemaRepository); + inlineSchema.Properties[JsonApiPropertyName.Type] = referenceSchema.WrapInExtendedSchema(); } } - private void AdaptResourceIdentity(OpenApiSchema fullSchema, ResourceSchemaType resourceSchemaType, bool forRequestSchema, + private void AdaptResourceIdentity(OpenApiSchema inlineSchema, ResourceSchemaType resourceSchemaType, bool forRequestSchema, SchemaRepository schemaRepository) { if (!forRequestSchema) @@ -297,7 +299,7 @@ private void AdaptResourceIdentity(OpenApiSchema fullSchema, ResourceSchemaType if (!hasAtomicOperationsEndpoint) { - fullSchema.Properties.Remove(JsonApiPropertyName.Lid); + inlineSchema.Properties?.Remove(JsonApiPropertyName.Lid); } if (resourceSchemaType.SchemaOpenType == typeof(DataInCreateRequest<>)) @@ -308,23 +310,25 @@ private void AdaptResourceIdentity(OpenApiSchema fullSchema, ResourceSchemaType { if (clientIdGeneration == ClientIdGenerationMode.Forbidden) { - fullSchema.Properties.Remove(JsonApiPropertyName.Id); + inlineSchema.Properties?.Remove(JsonApiPropertyName.Id); } else if (clientIdGeneration == ClientIdGenerationMode.Required) { - fullSchema.Properties.Remove(JsonApiPropertyName.Lid); - fullSchema.Required.Add(JsonApiPropertyName.Id); + inlineSchema.Properties?.Remove(JsonApiPropertyName.Lid); + inlineSchema.Required ??= new SortedSet(StringComparer.Ordinal); + inlineSchema.Required.Add(JsonApiPropertyName.Id); } } else { if (clientIdGeneration == ClientIdGenerationMode.Forbidden) { - fullSchema.Properties.Remove(JsonApiPropertyName.Id); + inlineSchema.Properties?.Remove(JsonApiPropertyName.Id); } else if (clientIdGeneration == ClientIdGenerationMode.Required) { - fullSchema.Required.Add(JsonApiPropertyName.Id); + inlineSchema.Required ??= new SortedSet(StringComparer.Ordinal); + inlineSchema.Required.Add(JsonApiPropertyName.Id); } } } @@ -332,60 +336,59 @@ private void AdaptResourceIdentity(OpenApiSchema fullSchema, ResourceSchemaType { if (!hasAtomicOperationsEndpoint) { - fullSchema.Required.Add(JsonApiPropertyName.Id); + inlineSchema.Required ??= new SortedSet(StringComparer.Ordinal); + inlineSchema.Required.Add(JsonApiPropertyName.Id); } } } - private void SetResourceId(OpenApiSchema fullSchema, ResourceType resourceType, SchemaRepository schemaRepository) + private void SetResourceId(OpenApiSchema inlineSchema, ResourceType resourceType, SchemaRepository schemaRepository) { - if (fullSchema.Properties.ContainsKey(JsonApiPropertyName.Id)) + if (inlineSchema.Properties != null && inlineSchema.Properties.ContainsKey(JsonApiPropertyName.Id)) { OpenApiSchema idSchema = _resourceIdSchemaGenerator.GenerateSchema(resourceType, schemaRepository); - fullSchema.Properties[JsonApiPropertyName.Id] = idSchema; + inlineSchema.Properties[JsonApiPropertyName.Id] = idSchema; } } - private void SetResourceFields(OpenApiSchema fullSchemaForData, ResourceSchemaType resourceSchemaType, bool forRequestSchema, - SchemaRepository schemaRepository) + private void SetResourceFields(OpenApiSchema inlineSchema, ResourceSchemaType resourceSchemaType, bool forRequestSchema, SchemaRepository schemaRepository) { - bool schemaHasFields = fullSchemaForData.Properties.ContainsKey(JsonApiPropertyName.Attributes) && - fullSchemaForData.Properties.ContainsKey(JsonApiPropertyName.Relationships); - - if (schemaHasFields) + if (inlineSchema.Properties != null && inlineSchema.Properties.ContainsKey(JsonApiPropertyName.Attributes) && + inlineSchema.Properties.ContainsKey(JsonApiPropertyName.Relationships)) { var fieldSchemaBuilder = new ResourceFieldSchemaBuilder(_schemaGenerationTracer, _defaultSchemaGenerator, this, _linksVisibilitySchemaGenerator, _resourceFieldValidationMetadataProvider, _relationshipTypeFactory, resourceSchemaType); - SetFieldSchemaMembers(fullSchemaForData, resourceSchemaType, forRequestSchema, true, fieldSchemaBuilder, schemaRepository); - SetFieldSchemaMembers(fullSchemaForData, resourceSchemaType, forRequestSchema, false, fieldSchemaBuilder, schemaRepository); + SetFieldSchemaMembers(inlineSchema.Properties, resourceSchemaType, forRequestSchema, true, fieldSchemaBuilder, schemaRepository); + SetFieldSchemaMembers(inlineSchema.Properties, resourceSchemaType, forRequestSchema, false, fieldSchemaBuilder, schemaRepository); } } - private void SetFieldSchemaMembers(OpenApiSchema fullSchemaForData, ResourceSchemaType resourceSchemaTypeForData, bool forRequestSchema, bool forAttributes, - ResourceFieldSchemaBuilder fieldSchemaBuilder, SchemaRepository schemaRepository) + private void SetFieldSchemaMembers(IDictionary inlineSchemaForDataProperties, ResourceSchemaType resourceSchemaTypeForData, + bool forRequestSchema, bool forAttributes, ResourceFieldSchemaBuilder fieldSchemaBuilder, SchemaRepository schemaRepository) { string propertyNameInSchema = forAttributes ? JsonApiPropertyName.Attributes : JsonApiPropertyName.Relationships; - OpenApiSchema referenceSchemaForFields = fullSchemaForData.Properties[propertyNameInSchema].UnwrapLastExtendedSchema(); - OpenApiSchema fullSchemaForFields = schemaRepository.Schemas[referenceSchemaForFields.Reference.Id]; - fullSchemaForFields.AdditionalPropertiesAllowed = false; + OpenApiSchemaReference referenceSchemaForFields = inlineSchemaForDataProperties[propertyNameInSchema].UnwrapLastExtendedSchema().AsReferenceSchema(); + OpenApiSchema inlineSchemaForFields = schemaRepository.Schemas[referenceSchemaForFields.GetReferenceId()].AsInlineSchema(); + inlineSchemaForFields.AdditionalPropertiesAllowed = false; - SetAbstract(fullSchemaForFields, resourceSchemaTypeForData); + SetAbstract(inlineSchemaForFields, resourceSchemaTypeForData); if (forAttributes) { - fieldSchemaBuilder.SetMembersOfAttributes(fullSchemaForFields, forRequestSchema, schemaRepository); + fieldSchemaBuilder.SetMembersOfAttributes(inlineSchemaForFields, forRequestSchema, schemaRepository); } else { - fieldSchemaBuilder.SetMembersOfRelationships(fullSchemaForFields, forRequestSchema, schemaRepository); + fieldSchemaBuilder.SetMembersOfRelationships(inlineSchemaForFields, forRequestSchema, schemaRepository); } - if (fullSchemaForFields.Properties.Count == 0 && !resourceSchemaTypeForData.ResourceType.IsPartOfTypeHierarchy()) + if ((inlineSchemaForFields.Properties == null || inlineSchemaForFields.Properties.Count == 0) && + !resourceSchemaTypeForData.ResourceType.IsPartOfTypeHierarchy()) { - fullSchemaForData.Properties.Remove(propertyNameInSchema); - schemaRepository.Schemas.Remove(referenceSchemaForFields.Reference.Id); + inlineSchemaForDataProperties.Remove(propertyNameInSchema); + schemaRepository.Schemas.Remove(referenceSchemaForFields.GetReferenceId()); } else { @@ -414,14 +417,14 @@ private void SetFieldSchemaMembers(OpenApiSchema fullSchemaForData, ResourceSche baseSchemaType = commonFieldsSchemaType; } - OpenApiSchema referenceSchemaForBase = schemaRepository.LookupByType(baseSchemaType); + OpenApiSchemaReference referenceSchemaForBase = schemaRepository.LookupByType(baseSchemaType); - schemaRepository.Schemas[referenceSchemaForFields.Reference.Id] = new OpenApiSchema + schemaRepository.Schemas[referenceSchemaForFields.GetReferenceId()] = new OpenApiSchema { AllOf = [ referenceSchemaForBase, - fullSchemaForFields + inlineSchemaForFields ], AdditionalPropertiesAllowed = false }; @@ -437,40 +440,39 @@ private ResourceSchemaType GetResourceSchemaTypeForFieldsProperty(ResourceSchema return ResourceSchemaType.Create(fieldsConstructedType, _resourceGraph); } - private OpenApiSchema GenerateSchemaForCommonFields(Type commonFieldsSchemaType, SchemaRepository schemaRepository) + private OpenApiSchemaReference GenerateSchemaForCommonFields(Type commonFieldsSchemaType, SchemaRepository schemaRepository) { - if (schemaRepository.TryLookupByType(commonFieldsSchemaType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(commonFieldsSchemaType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, commonFieldsSchemaType); - OpenApiSchema referenceSchemaForResourceType = _resourceTypeSchemaGenerator.GenerateSchema(schemaRepository); + OpenApiSchemaReference referenceSchemaForResourceType = _resourceTypeSchemaGenerator.GenerateSchema(schemaRepository); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "object", - Required = new SortedSet([OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName]), - Properties = new Dictionary + Type = JsonSchemaType.Object, + Required = new SortedSet([OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName], StringComparer.Ordinal), + Properties = new Dictionary { [OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName] = referenceSchemaForResourceType.WrapInExtendedSchema() }, AdditionalPropertiesAllowed = false, Discriminator = new OpenApiDiscriminator { - PropertyName = OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName, - Mapping = new SortedDictionary(StringComparer.Ordinal) + PropertyName = OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName }, - Extensions = + Extensions = new SortedDictionary(StringComparer.Ordinal) { - ["x-abstract"] = new OpenApiBoolean(true) + ["x-abstract"] = new JsonNodeExtension(true) } }; string schemaId = _schemaIdSelector.GetSchemaId(commonFieldsSchemaType); - referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(commonFieldsSchemaType, schemaId); traceScope.TraceSucceeded(schemaId); @@ -480,7 +482,7 @@ private OpenApiSchema GenerateSchemaForCommonFields(Type commonFieldsSchemaType, private void MapInDiscriminator(ResourceSchemaType resourceSchemaType, bool forRequestSchema, string discriminatorPropertyName, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchemaForDerived = schemaRepository.LookupByType(resourceSchemaType.SchemaConstructedType); + OpenApiSchemaReference referenceSchemaForDerived = schemaRepository.LookupByType(resourceSchemaType.SchemaConstructedType); foreach (ResourceType? baseResourceType in GetBaseTypesToMapInto(resourceSchemaType, forRequestSchema)) { @@ -488,23 +490,23 @@ private void MapInDiscriminator(ResourceSchemaType resourceSchemaType, bool forR ? GetCommonSchemaType(resourceSchemaType.SchemaOpenType)! : resourceSchemaType.ChangeResourceType(baseResourceType).SchemaConstructedType; - OpenApiSchema referenceSchemaForBase = schemaRepository.LookupByType(baseSchemaType); - OpenApiSchema inlineSchemaForBase = schemaRepository.Schemas[referenceSchemaForBase.Reference.Id].UnwrapLastExtendedSchema(); + OpenApiSchemaReference referenceSchemaForBase = schemaRepository.LookupByType(baseSchemaType); + OpenApiSchema inlineSchemaForBase = schemaRepository.Schemas[referenceSchemaForBase.GetReferenceId()].UnwrapLastExtendedSchema().AsInlineSchema(); inlineSchemaForBase.Discriminator ??= new OpenApiDiscriminator { - PropertyName = discriminatorPropertyName, - Mapping = new SortedDictionary(StringComparer.Ordinal) + PropertyName = discriminatorPropertyName }; if (RepeatDiscriminatorInResponseDerivedTypes && !forRequestSchema) { + inlineSchemaForBase.Required ??= new SortedSet(StringComparer.Ordinal); inlineSchemaForBase.Required.Add(discriminatorPropertyName); } string publicName = resourceSchemaType.ResourceType.PublicName; - if (inlineSchemaForBase.Discriminator.Mapping.TryAdd(publicName, referenceSchemaForDerived.Reference.ReferenceV3) && baseResourceType == null) + if (TryAddToDiscriminatorMapping(inlineSchemaForBase, publicName, referenceSchemaForDerived) && baseResourceType == null) { MapResourceTypeInEnum(publicName, schemaRepository); } @@ -540,30 +542,40 @@ private void MapInDiscriminator(ResourceSchemaType resourceSchemaType, bool forR } } + private static bool TryAddToDiscriminatorMapping(OpenApiSchema inlineSchema, string schemaId, OpenApiSchemaReference mappingValueReferenceSchema) + { + ConsistencyGuard.ThrowIf(inlineSchema.Discriminator is null); + + inlineSchema.Discriminator.Mapping ??= new SortedDictionary(StringComparer.Ordinal); + return inlineSchema.Discriminator.Mapping.TryAdd(schemaId, mappingValueReferenceSchema); + } + private void MapResourceTypeInEnum(string publicName, SchemaRepository schemaRepository) { string schemaId = _schemaIdSelector.GetResourceTypeSchemaId(null); - OpenApiSchema fullSchema = schemaRepository.Schemas[schemaId]; + OpenApiSchema inlineSchema = schemaRepository.Schemas[schemaId].AsInlineSchema(); + inlineSchema.Enum ??= new List(); - if (!fullSchema.Enum.Any(openApiAny => openApiAny is OpenApiString openApiString && openApiString.Value == publicName)) + if (!inlineSchema.Enum.Any(jsonNode => jsonNode is JsonValue jsonValue && jsonValue.GetValueKind() == JsonValueKind.String && + jsonValue.GetValue() == publicName)) { - fullSchema.Enum.Add(new OpenApiString(publicName)); + inlineSchema.Enum.Add(publicName); } } - private void SetDocumentation(OpenApiSchema fullSchema, ResourceType resourceType) + private void SetDocumentation(OpenApiSchema inlineSchema, ResourceType resourceType) { - fullSchema.Description = _resourceDocumentationReader.GetDocumentationForType(resourceType); + inlineSchema.Description = _resourceDocumentationReader.GetDocumentationForType(resourceType); } - private void SetLinksVisibility(OpenApiSchema fullSchema, ResourceSchemaType resourceSchemaType, SchemaRepository schemaRepository) + private void SetLinksVisibility(OpenApiSchema inlineSchema, ResourceSchemaType resourceSchemaType, SchemaRepository schemaRepository) { - _linksVisibilitySchemaGenerator.UpdateSchemaForResource(resourceSchemaType, fullSchema, schemaRepository); + _linksVisibilitySchemaGenerator.UpdateSchemaForResource(resourceSchemaType, inlineSchema, schemaRepository); } private void GenerateDataSchemasForDirectlyDerivedTypes(ResourceSchemaType resourceSchemaType, bool forRequestSchema, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchemaForBase = schemaRepository.LookupByType(resourceSchemaType.SchemaConstructedType); + OpenApiSchemaReference referenceSchemaForBase = schemaRepository.LookupByType(resourceSchemaType.SchemaConstructedType); foreach (ResourceType derivedType in resourceSchemaType.ResourceType.DirectlyDerivedTypes) { @@ -572,54 +584,58 @@ private void GenerateDataSchemasForDirectlyDerivedTypes(ResourceSchemaType resou using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, resourceSchemaTypeForDerived.SchemaConstructedType); - OpenApiSchema referenceSchemaForDerived = _defaultSchemaGenerator.GenerateSchema(derivedSchemaType, schemaRepository); - OpenApiSchema fullSchemaForDerived = schemaRepository.Schemas[referenceSchemaForDerived.Reference.Id]; - fullSchemaForDerived.AdditionalPropertiesAllowed = false; + OpenApiSchemaReference referenceSchemaForDerived = _defaultSchemaGenerator.GenerateSchema(derivedSchemaType, schemaRepository).AsReferenceSchema(); + OpenApiSchema possiblyCompositeInlineSchemaForDerived = schemaRepository.Schemas[referenceSchemaForDerived.GetReferenceId()].AsInlineSchema(); + possiblyCompositeInlineSchemaForDerived.AdditionalPropertiesAllowed = false; - OpenApiSchema inlineSchemaForDerived = fullSchemaForDerived.UnwrapLastExtendedSchema(); + OpenApiSchema inlineSchemaForDerived = possiblyCompositeInlineSchemaForDerived.UnwrapLastExtendedSchema().AsInlineSchema(); SetResourceFields(inlineSchemaForDerived, resourceSchemaTypeForDerived, forRequestSchema, schemaRepository); SetAbstract(inlineSchemaForDerived, resourceSchemaTypeForDerived); RemoveProperties(inlineSchemaForDerived); MapInDiscriminator(resourceSchemaTypeForDerived, forRequestSchema, JsonApiPropertyName.Type, schemaRepository); - if (fullSchemaForDerived.AllOf.Count == 0) + if (possiblyCompositeInlineSchemaForDerived.AllOf == null || possiblyCompositeInlineSchemaForDerived.AllOf.Count == 0) { - var compositeSchemaForDerived = new OpenApiSchema + var compositeInlineSchemaForDerived = new OpenApiSchema { AllOf = [ referenceSchemaForBase, - fullSchemaForDerived + possiblyCompositeInlineSchemaForDerived ], AdditionalPropertiesAllowed = false }; - schemaRepository.Schemas[referenceSchemaForDerived.Reference.Id] = compositeSchemaForDerived; + schemaRepository.Schemas[referenceSchemaForDerived.GetReferenceId()] = compositeInlineSchemaForDerived; } else { - fullSchemaForDerived.AllOf[0] = referenceSchemaForBase; + possiblyCompositeInlineSchemaForDerived.AllOf[0] = referenceSchemaForBase; } if (RequiresRootObjectTypeInDataSchema(resourceSchemaTypeForDerived, forRequestSchema)) { - OpenApiSchema fullSchemaForData = schemaRepository.Schemas[referenceSchemaForDerived.Reference.Id]; - fullSchemaForData.Extensions[SetSchemaTypeToObjectDocumentFilter.RequiresRootObjectTypeKey] = new OpenApiBoolean(true); + OpenApiSchema inlineSchemaForData = schemaRepository.Schemas[referenceSchemaForDerived.GetReferenceId()].AsInlineSchema(); + inlineSchemaForData.Extensions ??= new SortedDictionary(StringComparer.Ordinal); + inlineSchemaForData.Extensions[SetSchemaTypeToObjectDocumentFilter.RequiresRootObjectTypeKey] = new JsonNodeExtension(true); } GenerateDataSchemasForDirectlyDerivedTypes(resourceSchemaTypeForDerived, forRequestSchema, schemaRepository); - traceScope.TraceSucceeded(referenceSchemaForDerived.Reference.Id); + traceScope.TraceSucceeded(referenceSchemaForDerived.GetReferenceId()); } } - private static void RemoveProperties(OpenApiSchema fullSchema) + private static void RemoveProperties(OpenApiSchema inlineSchema) { - foreach (string propertyName in fullSchema.Properties.Keys) + if (inlineSchema.Properties != null) { - fullSchema.Properties.Remove(propertyName); - fullSchema.Required.Remove(propertyName); + foreach (string propertyName in inlineSchema.Properties.Keys) + { + inlineSchema.Properties.Remove(propertyName); + inlineSchema.Required?.Remove(propertyName); + } } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/LinksVisibilitySchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/LinksVisibilitySchemaGenerator.cs index 5c099a3fc1..80229d6025 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/LinksVisibilitySchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/LinksVisibilitySchemaGenerator.cs @@ -4,7 +4,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -70,72 +70,77 @@ public LinksVisibilitySchemaGenerator(IJsonApiOptions options, IResourceGraph re _lazyLinksVisibility = new Lazy(() => new LinksVisibility(options, resourceGraph), LazyThreadSafetyMode.ExecutionAndPublication); } - public void UpdateSchemaForTopLevel(Type schemaType, OpenApiSchema fullSchemaForLinksContainer, SchemaRepository schemaRepository) + public void UpdateSchemaForTopLevel(Type schemaType, OpenApiSchema inlineSchemaForLinksContainer, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); - ArgumentNullException.ThrowIfNull(fullSchemaForLinksContainer); + ArgumentNullException.ThrowIfNull(inlineSchemaForLinksContainer); Type lookupType = schemaType.ConstructedToOpenType(); if (LinksInJsonApiSchemaTypes.TryGetValue(lookupType, out LinkTypes possibleLinkTypes)) { - UpdateLinksProperty(fullSchemaForLinksContainer, _lazyLinksVisibility.Value.TopLevelLinks, possibleLinkTypes, schemaRepository); + UpdateLinksProperty(inlineSchemaForLinksContainer, _lazyLinksVisibility.Value.TopLevelLinks, possibleLinkTypes, schemaRepository); } } - public void UpdateSchemaForResource(ResourceSchemaType resourceSchemaType, OpenApiSchema fullSchemaForResourceData, SchemaRepository schemaRepository) + public void UpdateSchemaForResource(ResourceSchemaType resourceSchemaType, OpenApiSchema inlineSchemaForResourceData, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(resourceSchemaType); - ArgumentNullException.ThrowIfNull(fullSchemaForResourceData); + ArgumentNullException.ThrowIfNull(inlineSchemaForResourceData); if (LinksInJsonApiSchemaTypes.TryGetValue(resourceSchemaType.SchemaOpenType, out LinkTypes possibleLinkTypes)) { - UpdateLinksProperty(fullSchemaForResourceData, _lazyLinksVisibility.Value.ResourceLinks, possibleLinkTypes, schemaRepository); + UpdateLinksProperty(inlineSchemaForResourceData, _lazyLinksVisibility.Value.ResourceLinks, possibleLinkTypes, schemaRepository); } } - public void UpdateSchemaForRelationship(Type schemaType, OpenApiSchema fullSchemaForRelationship, SchemaRepository schemaRepository) + public void UpdateSchemaForRelationship(Type schemaType, OpenApiSchema inlineSchemaForRelationship, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); - ArgumentNullException.ThrowIfNull(fullSchemaForRelationship); + ArgumentNullException.ThrowIfNull(inlineSchemaForRelationship); Type lookupType = schemaType.ConstructedToOpenType(); if (LinksInJsonApiSchemaTypes.TryGetValue(lookupType, out LinkTypes possibleLinkTypes)) { - UpdateLinksProperty(fullSchemaForRelationship, _lazyLinksVisibility.Value.RelationshipLinks, possibleLinkTypes, schemaRepository); + UpdateLinksProperty(inlineSchemaForRelationship, _lazyLinksVisibility.Value.RelationshipLinks, possibleLinkTypes, schemaRepository); } } - private void UpdateLinksProperty(OpenApiSchema fullSchemaForLinksContainer, LinkTypes visibleLinkTypes, LinkTypes possibleLinkTypes, + private void UpdateLinksProperty(OpenApiSchema inlineSchemaForLinksContainer, LinkTypes visibleLinkTypes, LinkTypes possibleLinkTypes, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchemaForLinks = fullSchemaForLinksContainer.Properties[JsonApiPropertyName.Links].UnwrapLastExtendedSchema(); - - if ((visibleLinkTypes & possibleLinkTypes) == 0) + if (inlineSchemaForLinksContainer.Properties != null) { - fullSchemaForLinksContainer.Required.Remove(JsonApiPropertyName.Links); - fullSchemaForLinksContainer.Properties.Remove(JsonApiPropertyName.Links); + OpenApiSchemaReference referenceSchemaForLinks = + inlineSchemaForLinksContainer.Properties[JsonApiPropertyName.Links].UnwrapLastExtendedSchema().AsReferenceSchema(); - schemaRepository.Schemas.Remove(referenceSchemaForLinks.Reference.Id); - } - else if (visibleLinkTypes != possibleLinkTypes) - { - string linksSchemaId = referenceSchemaForLinks.Reference.Id; + if ((visibleLinkTypes & possibleLinkTypes) == 0) + { + inlineSchemaForLinksContainer.Required?.Remove(JsonApiPropertyName.Links); + inlineSchemaForLinksContainer.Properties.Remove(JsonApiPropertyName.Links); - if (schemaRepository.Schemas.TryGetValue(linksSchemaId, out OpenApiSchema? fullSchemaForLinks)) + schemaRepository.Schemas.Remove(referenceSchemaForLinks.GetReferenceId()); + } + else if (visibleLinkTypes != possibleLinkTypes) { - UpdateLinkProperties(fullSchemaForLinks, visibleLinkTypes); + string linksSchemaId = referenceSchemaForLinks.GetReferenceId(); + + if (schemaRepository.Schemas.TryGetValue(linksSchemaId, out IOpenApiSchema? schemaForLinks)) + { + OpenApiSchema inlineSchemaForLinks = schemaForLinks.AsInlineSchema(); + UpdateLinkProperties(inlineSchemaForLinks, visibleLinkTypes); + } } } } - private void UpdateLinkProperties(OpenApiSchema fullSchemaForLinks, LinkTypes availableLinkTypes) + private void UpdateLinkProperties(OpenApiSchema inlineSchemaForLinks, LinkTypes availableLinkTypes) { foreach (string propertyName in LinkTypeToPropertyNamesMap.Where(pair => !availableLinkTypes.HasFlag(pair.Key)).SelectMany(pair => pair.Value)) { - fullSchemaForLinks.Required.Remove(propertyName); - fullSchemaForLinks.Properties.Remove(propertyName); + inlineSchemaForLinks.Required?.Remove(propertyName); + inlineSchemaForLinks.Properties?.Remove(propertyName); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/MetaSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/MetaSchemaGenerator.cs index 1e1bd07852..83c4a0836f 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/MetaSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/MetaSchemaGenerator.cs @@ -1,5 +1,5 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -19,29 +19,29 @@ public MetaSchemaGenerator(SchemaGenerationTracer schemaGenerationTracer, JsonAp _schemaIdSelector = schemaIdSelector; } - public OpenApiSchema GenerateSchema(SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(SchemaType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(SchemaType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, SchemaType); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalProperties = new OpenApiSchema { - Nullable = true + Type = JsonSchemaType.Null } }; string schemaId = _schemaIdSelector.GetMetaSchemaId(); - referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(SchemaType, schemaId); traceScope.TraceSucceeded(schemaId); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs index 98f176e8df..fb50e61900 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipIdentifierSchemaGenerator.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -34,7 +34,7 @@ public RelationshipIdentifierSchemaGenerator(SchemaGenerationTracer schemaGenera _schemaIdSelector = schemaIdSelector; } - public OpenApiSchema GenerateSchema(RelationshipAttribute relationship, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(RelationshipAttribute relationship, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(relationship); ArgumentNullException.ThrowIfNull(schemaRepository); @@ -43,52 +43,48 @@ public OpenApiSchema GenerateSchema(RelationshipAttribute relationship, SchemaRe if (schemaRepository.Schemas.ContainsKey(schemaId)) { - return new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = schemaId, - Type = ReferenceType.Schema - } - }; + return new OpenApiSchemaReference(schemaId); } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, relationship); Type relationshipIdentifierConstructedType = typeof(RelationshipIdentifier<>).MakeGenericType(relationship.LeftType.ClrType); - ConsistencyGuard.ThrowIf(schemaRepository.TryLookupByType(relationshipIdentifierConstructedType, out _)); + ConsistencyGuard.ThrowIf(schemaRepository.TryLookupByTypeSafe(relationshipIdentifierConstructedType, out _)); - OpenApiSchema referenceSchemaForIdentifier = _defaultSchemaGenerator.GenerateSchema(relationshipIdentifierConstructedType, schemaRepository); - OpenApiSchema fullSchemaForIdentifier = schemaRepository.Schemas[referenceSchemaForIdentifier.Reference.Id]; + OpenApiSchemaReference referenceSchemaForIdentifier = + _defaultSchemaGenerator.GenerateSchema(relationshipIdentifierConstructedType, schemaRepository).AsReferenceSchema(); - fullSchemaForIdentifier.Properties.Remove(JsonApiPropertyName.Meta); + OpenApiSchema inlineSchemaForIdentifier = schemaRepository.Schemas[referenceSchemaForIdentifier.GetReferenceId()].AsInlineSchema(); - SetResourceType(fullSchemaForIdentifier, relationship.LeftType, schemaRepository); - SetResourceId(fullSchemaForIdentifier, relationship.LeftType, schemaRepository); - SetRelationship(fullSchemaForIdentifier, relationship, schemaRepository); + inlineSchemaForIdentifier.Properties ??= new Dictionary(); + inlineSchemaForIdentifier.Properties.Remove(JsonApiPropertyName.Meta); + + SetResourceType(inlineSchemaForIdentifier.Properties, relationship.LeftType, schemaRepository); + SetResourceId(inlineSchemaForIdentifier.Properties, relationship.LeftType, schemaRepository); + SetRelationship(inlineSchemaForIdentifier.Properties, relationship, schemaRepository); schemaRepository.ReplaceSchemaId(relationshipIdentifierConstructedType, schemaId); - referenceSchemaForIdentifier.Reference.Id = schemaId; + referenceSchemaForIdentifier = new OpenApiSchemaReference(schemaId); traceScope.TraceSucceeded(schemaId); return referenceSchemaForIdentifier; } - private void SetResourceType(OpenApiSchema fullSchemaForIdentifier, ResourceType resourceType, SchemaRepository schemaRepository) + private void SetResourceType(IDictionary schemaProperties, ResourceType resourceType, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchema = _resourceTypeSchemaGenerator.GenerateSchema(resourceType, schemaRepository); - fullSchemaForIdentifier.Properties[JsonApiPropertyName.Type] = referenceSchema.WrapInExtendedSchema(); + OpenApiSchemaReference referenceSchema = _resourceTypeSchemaGenerator.GenerateSchema(resourceType, schemaRepository); + schemaProperties[JsonApiPropertyName.Type] = referenceSchema.WrapInExtendedSchema(); } - private void SetResourceId(OpenApiSchema fullSchemaForResourceData, ResourceType resourceType, SchemaRepository schemaRepository) + private void SetResourceId(IDictionary schemaProperties, ResourceType resourceType, SchemaRepository schemaRepository) { OpenApiSchema idSchema = _resourceIdSchemaGenerator.GenerateSchema(resourceType, schemaRepository); - fullSchemaForResourceData.Properties[JsonApiPropertyName.Id] = idSchema; + schemaProperties[JsonApiPropertyName.Id] = idSchema; } - private void SetRelationship(OpenApiSchema fullSchemaForIdentifier, RelationshipAttribute relationship, SchemaRepository schemaRepository) + private void SetRelationship(IDictionary schemaProperties, RelationshipAttribute relationship, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchema = _relationshipNameSchemaGenerator.GenerateSchema(relationship, schemaRepository); - fullSchemaForIdentifier.Properties[JsonApiPropertyName.Relationship] = referenceSchema.WrapInExtendedSchema(); + OpenApiSchemaReference referenceSchema = _relationshipNameSchemaGenerator.GenerateSchema(relationship, schemaRepository); + schemaProperties[JsonApiPropertyName.Relationship] = referenceSchema.WrapInExtendedSchema(); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipNameSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipNameSchemaGenerator.cs index 7f5c0c5d3a..38d47c81f6 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipNameSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/RelationshipNameSchemaGenerator.cs @@ -1,6 +1,5 @@ using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -19,7 +18,7 @@ public RelationshipNameSchemaGenerator(SchemaGenerationTracer schemaGenerationTr _schemaIdSelector = schemaIdSelector; } - public OpenApiSchema GenerateSchema(RelationshipAttribute relationship, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(RelationshipAttribute relationship, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(relationship); ArgumentNullException.ThrowIfNull(schemaRepository); @@ -28,25 +27,18 @@ public OpenApiSchema GenerateSchema(RelationshipAttribute relationship, SchemaRe if (schemaRepository.Schemas.ContainsKey(schemaId)) { - return new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = schemaId, - Type = ReferenceType.Schema - } - }; + return new OpenApiSchemaReference(schemaId); } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, relationship); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "string", - Enum = [new OpenApiString(relationship.PublicName)] + Type = JsonSchemaType.String, + Enum = [relationship.PublicName] }; - OpenApiSchema referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + OpenApiSchemaReference referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); traceScope.TraceSucceeded(schemaId); return referenceSchema; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceIdSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceIdSchemaGenerator.cs index f361db8183..09e38f6545 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceIdSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceIdSchemaGenerator.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.OpenApi.Swashbuckle.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -40,10 +40,8 @@ public OpenApiSchema GenerateSchema(ResourceType resourceType, SchemaRepository ArgumentNullException.ThrowIfNull(resourceType); ArgumentNullException.ThrowIfNull(schemaRepository); - OpenApiSchema idSchema = _defaultSchemaGenerator.GenerateSchema(resourceType.IdentityClrType, schemaRepository); - ConsistencyGuard.ThrowIf(idSchema.Reference != null); - - idSchema.Type = "string"; + OpenApiSchema idSchema = _defaultSchemaGenerator.GenerateSchema(resourceType.IdentityClrType, schemaRepository).AsInlineSchema(); + idSchema.Type = JsonSchemaType.String; var hideIdTypeAttribute = resourceType.ClrType.GetCustomAttribute(); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceTypeSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceTypeSchemaGenerator.cs index eb933bedbe..8eb4771cb7 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceTypeSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Components/ResourceTypeSchemaGenerator.cs @@ -1,7 +1,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; @@ -20,70 +19,63 @@ public ResourceTypeSchemaGenerator(SchemaGenerationTracer schemaGenerationTracer _schemaIdSelector = schemaIdSelector; } - public OpenApiSchema GenerateSchema(ResourceType resourceType, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(ResourceType resourceType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(resourceType); ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(resourceType.ClrType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(resourceType.ClrType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, resourceType.ClrType); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "string", - Enum = resourceType.ClrType.IsAbstract ? [] : [new OpenApiString(resourceType.PublicName)], - Extensions = + Type = JsonSchemaType.String, + Enum = resourceType.ClrType.IsAbstract ? [] : [resourceType.PublicName], + Extensions = new SortedDictionary(StringComparer.Ordinal) { - [StringEnumOrderingFilter.RequiresSortKey] = new OpenApiBoolean(true) + [StringEnumOrderingFilter.RequiresSortKey] = new JsonNodeExtension(true) } }; foreach (ResourceType derivedType in resourceType.GetAllConcreteDerivedTypes()) { - fullSchema.Enum.Add(new OpenApiString(derivedType.PublicName)); + inlineSchema.Enum.Add(derivedType.PublicName); } string schemaId = _schemaIdSelector.GetResourceTypeSchemaId(resourceType); - referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(resourceType.ClrType, schemaId); traceScope.TraceSucceeded(schemaId); return referenceSchema; } - public OpenApiSchema GenerateSchema(SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(SchemaRepository schemaRepository) { string schemaId = _schemaIdSelector.GetResourceTypeSchemaId(null); if (schemaRepository.Schemas.ContainsKey(schemaId)) { - return new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = schemaId, - Type = ReferenceType.Schema - } - }; + return new OpenApiSchemaReference(schemaId); } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "string", - Extensions = + Type = JsonSchemaType.String, + Extensions = new SortedDictionary(StringComparer.Ordinal) { - [StringEnumOrderingFilter.RequiresSortKey] = new OpenApiBoolean(true) + [StringEnumOrderingFilter.RequiresSortKey] = new JsonNodeExtension(true) } }; - OpenApiSchema referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + OpenApiSchemaReference referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); traceScope.TraceSucceeded(schemaId); return referenceSchema; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs index 648831fcbb..54a5b25179 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs @@ -7,8 +7,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; using JsonApiDotNetCore.Resources.Annotations; using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Documents; @@ -68,7 +67,7 @@ public override bool CanGenerate(Type schemaType) return schemaType == typeof(OperationsRequestDocument) || schemaType == typeof(OperationsResponseDocument); } - protected override OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) + protected override OpenApiSchemaReference GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); ArgumentNullException.ThrowIfNull(schemaRepository); @@ -84,7 +83,7 @@ protected override OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaR GenerateSchemasForResponseDocument(schemaRepository); } - return _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository); + return _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository).AsReferenceSchema(); } private void GenerateSchemasForRequestDocument(SchemaRepository schemaRepository) @@ -97,44 +96,43 @@ private void GenerateSchemasForRequestDocument(SchemaRepository schemaRepository } } - private OpenApiSchema GenerateSchemaForAbstractOperation(SchemaRepository schemaRepository) + private OpenApiSchemaReference GenerateSchemaForAbstractOperation(SchemaRepository schemaRepository) { - if (schemaRepository.TryLookupByType(AtomicOperationAbstractType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(AtomicOperationAbstractType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, AtomicOperationAbstractType); - OpenApiSchema referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); + OpenApiSchemaReference referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); - var fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "object", - Required = new SortedSet([OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName]), - Properties = new Dictionary + Type = JsonSchemaType.Object, + Required = new SortedSet([OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName], StringComparer.Ordinal), + Properties = new Dictionary { - [OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName] = new() + [OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName] = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }, - [referenceSchemaForMeta.Reference.Id] = referenceSchemaForMeta.WrapInExtendedSchema() + [referenceSchemaForMeta.GetReferenceId()] = referenceSchemaForMeta.WrapInExtendedSchema() }, AdditionalPropertiesAllowed = false, Discriminator = new OpenApiDiscriminator { - PropertyName = OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName, - Mapping = new SortedDictionary(StringComparer.Ordinal) + PropertyName = OpenApiMediaTypeExtension.FullyQualifiedOpenApiDiscriminatorPropertyName }, - Extensions = + Extensions = new SortedDictionary(StringComparer.Ordinal) { - ["x-abstract"] = new OpenApiBoolean(true) + ["x-abstract"] = new JsonNodeExtension(true) } }; string schemaId = _schemaIdSelector.GetSchemaId(AtomicOperationAbstractType); - referenceSchema = schemaRepository.AddDefinition(schemaId, fullSchema); + referenceSchema = schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(AtomicOperationAbstractType, schemaId); traceScope.TraceSucceeded(schemaId); @@ -184,22 +182,25 @@ private void GenerateSchemaForResourceOperation(Type operationOpenType, Resource if (hasDataProperty) { - _ = _dataContainerSchemaGenerator.GenerateSchema(operationConstructedType, resourceType, true, false, schemaRepository); + _dataContainerSchemaGenerator.GenerateSchema(operationConstructedType, resourceType, true, false, schemaRepository); } } - OpenApiSchema referenceSchemaForOperation = _defaultSchemaGenerator.GenerateSchema(operationConstructedType, schemaRepository); - OpenApiSchema fullSchemaForOperation = schemaRepository.Schemas[referenceSchemaForOperation.Reference.Id]; - fullSchemaForOperation.AdditionalPropertiesAllowed = false; - OpenApiSchema inlineSchemaForOperation = fullSchemaForOperation.UnwrapLastExtendedSchema(); + OpenApiSchemaReference referenceSchemaForOperation = + _defaultSchemaGenerator.GenerateSchema(operationConstructedType, schemaRepository).AsReferenceSchema(); + + OpenApiSchema compositeInlineSchemaForOperation = schemaRepository.Schemas[referenceSchemaForOperation.GetReferenceId()].AsInlineSchema(); + compositeInlineSchemaForOperation.AdditionalPropertiesAllowed = false; + OpenApiSchema inlineSchemaForOperation = compositeInlineSchemaForOperation.UnwrapLastExtendedSchema().AsInlineSchema(); if (needsEmptyDerivedSchema) { Type baseOperationSchemaType = ChangeResourceTypeInSchemaType(operationOpenType, resourceType.BaseType!); - OpenApiSchema referenceSchemaForBaseOperation = schemaRepository.LookupByType(baseOperationSchemaType); + OpenApiSchemaReference referenceSchemaForBaseOperation = schemaRepository.LookupByType(baseOperationSchemaType); RemoveProperties(inlineSchemaForOperation); - fullSchemaForOperation.AllOf[0] = referenceSchemaForBaseOperation; + compositeInlineSchemaForOperation.AllOf ??= new List(); + compositeInlineSchemaForOperation.AllOf[0] = referenceSchemaForBaseOperation; } else { @@ -208,7 +209,7 @@ private void GenerateSchemaForResourceOperation(Type operationOpenType, Resource MapInDiscriminator(referenceSchemaForOperation, schemaRepository); - traceScope.TraceSucceeded(referenceSchemaForOperation.Reference.Id); + traceScope.TraceSucceeded(referenceSchemaForOperation.GetReferenceId()); } foreach (ResourceType derivedType in resourceType.DirectlyDerivedTypes) @@ -248,26 +249,38 @@ private static Type ChangeResourceTypeInSchemaType(Type schemaOpenType, Resource return schemaOpenType.MakeGenericType(resourceType.ClrType); } - private static void RemoveProperties(OpenApiSchema fullSchema) + private static void RemoveProperties(OpenApiSchema inlineSchema) { - foreach (string propertyName in fullSchema.Properties.Keys) + if (inlineSchema.Properties != null) { - fullSchema.Properties.Remove(propertyName); - fullSchema.Required.Remove(propertyName); + foreach (string propertyName in inlineSchema.Properties.Keys) + { + inlineSchema.Properties.Remove(propertyName); + inlineSchema.Required?.Remove(propertyName); + } } } - private void SetOperationCode(OpenApiSchema fullSchema, AtomicOperationCode operationCode, SchemaRepository schemaRepository) + private void SetOperationCode(OpenApiSchema inlineSchema, AtomicOperationCode operationCode, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchema = _atomicOperationCodeSchemaGenerator.GenerateSchema(operationCode, schemaRepository); - fullSchema.Properties[JsonApiPropertyName.Op] = referenceSchema.WrapInExtendedSchema(); + OpenApiSchemaReference referenceSchema = _atomicOperationCodeSchemaGenerator.GenerateSchema(operationCode, schemaRepository); + inlineSchema.Properties ??= new Dictionary(); + inlineSchema.Properties[JsonApiPropertyName.Op] = referenceSchema.WrapInExtendedSchema(); } - private static void MapInDiscriminator(OpenApiSchema referenceSchemaForOperation, SchemaRepository schemaRepository) + private static void MapInDiscriminator(OpenApiSchemaReference referenceSchemaForOperation, SchemaRepository schemaRepository) { - OpenApiSchema referenceSchemaForAbstractOperation = schemaRepository.LookupByType(AtomicOperationAbstractType); - OpenApiSchema fullSchemaForAbstractOperation = schemaRepository.Schemas[referenceSchemaForAbstractOperation.Reference.Id]; - fullSchemaForAbstractOperation.Discriminator.Mapping.Add(referenceSchemaForOperation.Reference.Id, referenceSchemaForOperation.Reference.ReferenceV3); + OpenApiSchemaReference referenceSchemaForAbstractOperation = schemaRepository.LookupByType(AtomicOperationAbstractType); + OpenApiSchema inlineSchemaForAbstractOperation = schemaRepository.Schemas[referenceSchemaForAbstractOperation.GetReferenceId()].AsInlineSchema(); + AddToDiscriminatorMapping(inlineSchemaForAbstractOperation, referenceSchemaForOperation.GetReferenceId(), referenceSchemaForOperation); + } + + private static void AddToDiscriminatorMapping(OpenApiSchema inlineSchema, string schemaId, OpenApiSchemaReference mappingValueReferenceSchema) + { + ConsistencyGuard.ThrowIf(inlineSchema.Discriminator is null); + + inlineSchema.Discriminator.Mapping ??= new SortedDictionary(StringComparer.Ordinal); + inlineSchema.Discriminator.Mapping.Add(schemaId, mappingValueReferenceSchema); } private static HashSet GetRelationshipsInTypeHierarchy(ResourceType baseType) @@ -314,7 +327,7 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela RelationshipAttribute? relationshipInAnyBaseResourceType = GetRelationshipEnabledInAnyBase(relationship, writeOperation); - OpenApiSchema? referenceSchemaForRelationshipIdentifier; + OpenApiSchemaReference? referenceSchemaForRelationshipIdentifier; if (relationshipInAnyBaseResourceType == null) { @@ -329,29 +342,33 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela } Type operationConstructedType = ChangeResourceTypeInSchemaType(operationOpenType, relationship.RightType); - _ = _dataContainerSchemaGenerator.GenerateSchema(operationConstructedType, relationship.RightType, true, false, schemaRepository); + _dataContainerSchemaGenerator.GenerateSchema(operationConstructedType, relationship.RightType, true, false, schemaRepository); // This complicated implementation that generates a temporary schema stems from the fact that GetSchemaId takes a Type. // We could feed it a constructed type with TLeftResource and TRightResource, but there's no way to include // the relationship name because there's no runtime Type available for it. string schemaId = _schemaIdSelector.GetRelationshipAtomicOperationSchemaId(relationship, operationCode); - OpenApiSchema referenceSchemaForOperation = _defaultSchemaGenerator.GenerateSchema(operationConstructedType, schemaRepository); - OpenApiSchema fullSchemaForOperation = schemaRepository.Schemas[referenceSchemaForOperation.Reference.Id]; - fullSchemaForOperation.AdditionalPropertiesAllowed = false; + OpenApiSchemaReference referenceSchemaForOperation = + _defaultSchemaGenerator.GenerateSchema(operationConstructedType, schemaRepository).AsReferenceSchema(); + + OpenApiSchema compositeInlineSchemaForOperation = schemaRepository.Schemas[referenceSchemaForOperation.GetReferenceId()].AsInlineSchema(); + compositeInlineSchemaForOperation.AdditionalPropertiesAllowed = false; - OpenApiSchema inlineSchemaForOperation = fullSchemaForOperation.UnwrapLastExtendedSchema(); + OpenApiSchema inlineSchemaForOperation = compositeInlineSchemaForOperation.UnwrapLastExtendedSchema().AsInlineSchema(); SetOperationCode(inlineSchemaForOperation, operationCode, schemaRepository); + inlineSchemaForOperation.Properties ??= new Dictionary(); if (referenceSchemaForRelationshipIdentifier != null) { inlineSchemaForOperation.Properties[JsonApiPropertyName.Ref] = referenceSchemaForRelationshipIdentifier.WrapInExtendedSchema(); } - inlineSchemaForOperation.Properties[JsonApiPropertyName.Data].Nullable = _resourceFieldValidationMetadataProvider.IsNullable(relationship); + bool isNullable = _resourceFieldValidationMetadataProvider.IsNullable(relationship); + ((OpenApiSchema)inlineSchemaForOperation.Properties[JsonApiPropertyName.Data]).SetNullable(isNullable); schemaRepository.ReplaceSchemaId(operationConstructedType, schemaId); - referenceSchemaForOperation.Reference.Id = schemaId; + referenceSchemaForOperation = new OpenApiSchemaReference(schemaId); if (relationshipInAnyBaseResourceType != null) { @@ -360,14 +377,8 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela string baseRelationshipSchemaId = _schemaIdSelector.GetRelationshipAtomicOperationSchemaId(relationshipInAnyBaseResourceType, operationCode); ConsistencyGuard.ThrowIf(!schemaRepository.Schemas.ContainsKey(baseRelationshipSchemaId)); - fullSchemaForOperation.AllOf[0] = new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = baseRelationshipSchemaId, - Type = ReferenceType.Schema - } - }; + compositeInlineSchemaForOperation.AllOf ??= new List(); + compositeInlineSchemaForOperation.AllOf[0] = new OpenApiSchemaReference(baseRelationshipSchemaId); } MapInDiscriminator(referenceSchemaForOperation, schemaRepository); @@ -476,7 +487,7 @@ private void GenerateSchemasForResponseDocument(SchemaRepository schemaRepositor if (IsResourceTypeEnabled(resourceType, WriteOperationKind.CreateResource) || IsResourceTypeEnabled(resourceType, WriteOperationKind.UpdateResource)) { - _ = _dataContainerSchemaGenerator.GenerateSchema(typeof(AtomicResult), resourceType, false, false, schemaRepository); + _dataContainerSchemaGenerator.GenerateSchema(typeof(AtomicResult), resourceType, false, false, schemaRepository); } } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/DocumentSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/DocumentSchemaGenerator.cs index 740b2fca43..6ab806485f 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/DocumentSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/DocumentSchemaGenerator.cs @@ -1,6 +1,6 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Documents; @@ -31,12 +31,12 @@ protected DocumentSchemaGenerator(SchemaGenerationTracer schemaGenerationTracer, public abstract bool CanGenerate(Type schemaType); - public OpenApiSchema GenerateSchema(Type schemaType, SchemaRepository schemaRepository) + public OpenApiSchemaReference GenerateSchema(Type schemaType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); ArgumentNullException.ThrowIfNull(schemaRepository); - if (schemaRepository.TryLookupByType(schemaType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(schemaType, out OpenApiSchemaReference? referenceSchema)) { return referenceSchema; } @@ -46,23 +46,22 @@ public OpenApiSchema GenerateSchema(Type schemaType, SchemaRepository schemaRepo _metaSchemaGenerator.GenerateSchema(schemaRepository); referenceSchema = GenerateDocumentSchema(schemaType, schemaRepository); - OpenApiSchema fullSchema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + OpenApiSchema inlineSchema = schemaRepository.Schemas[referenceSchema.GetReferenceId()].AsInlineSchema(); - _linksVisibilitySchemaGenerator.UpdateSchemaForTopLevel(schemaType, fullSchema, schemaRepository); + _linksVisibilitySchemaGenerator.UpdateSchemaForTopLevel(schemaType, inlineSchema, schemaRepository); - SetJsonApiVersion(fullSchema, schemaRepository); + SetJsonApiVersion(inlineSchema, schemaRepository); - traceScope.TraceSucceeded(referenceSchema.Reference.Id); + traceScope.TraceSucceeded(referenceSchema.GetReferenceId()); return referenceSchema; } - protected abstract OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository); + protected abstract OpenApiSchemaReference GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository); - private void SetJsonApiVersion(OpenApiSchema fullSchema, SchemaRepository schemaRepository) + private void SetJsonApiVersion(OpenApiSchema inlineSchema, SchemaRepository schemaRepository) { - if (fullSchema.Properties.ContainsKey(JsonApiPropertyName.Jsonapi) && !_options.IncludeJsonApiVersion) + if (!_options.IncludeJsonApiVersion && inlineSchema.Properties != null && inlineSchema.Properties.Remove(JsonApiPropertyName.Jsonapi)) { - fullSchema.Properties.Remove(JsonApiPropertyName.Jsonapi); schemaRepository.Schemas.Remove(JsonApiPropertyName.Jsonapi); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ErrorResponseDocumentSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ErrorResponseDocumentSchemaGenerator.cs index 3d305e889b..57a4d2ff26 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ErrorResponseDocumentSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ErrorResponseDocumentSchemaGenerator.cs @@ -2,7 +2,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.Documents; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; using JsonApiDotNetCore.Serialization.Objects; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Documents; @@ -34,27 +34,28 @@ public override bool CanGenerate(Type schemaType) return schemaType == typeof(ErrorResponseDocument); } - protected override OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) + protected override OpenApiSchemaReference GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); ArgumentNullException.ThrowIfNull(schemaRepository); - OpenApiSchema referenceSchemaForErrorObject = GenerateSchemaForErrorObject(schemaRepository); - OpenApiSchema fullSchemaForErrorObject = schemaRepository.Schemas[referenceSchemaForErrorObject.Reference.Id]; + OpenApiSchemaReference referenceSchemaForErrorObject = GenerateSchemaForErrorObject(schemaRepository); + OpenApiSchema inlineSchemaForErrorObject = schemaRepository.Schemas[referenceSchemaForErrorObject.GetReferenceId()].AsInlineSchema(); - OpenApiSchema referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); - fullSchemaForErrorObject.Properties[JsonApiPropertyName.Meta] = referenceSchemaForMeta.WrapInExtendedSchema(); + OpenApiSchemaReference referenceSchemaForMeta = _metaSchemaGenerator.GenerateSchema(schemaRepository); + inlineSchemaForErrorObject.Properties ??= new Dictionary(); + inlineSchemaForErrorObject.Properties[JsonApiPropertyName.Meta] = referenceSchemaForMeta.WrapInExtendedSchema(); - return _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository); + return _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository).AsReferenceSchema(); } - private OpenApiSchema GenerateSchemaForErrorObject(SchemaRepository schemaRepository) + private OpenApiSchemaReference GenerateSchemaForErrorObject(SchemaRepository schemaRepository) { using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, ErrorObjectType); - OpenApiSchema referenceSchema = _defaultSchemaGenerator.GenerateSchema(ErrorObjectType, schemaRepository); + OpenApiSchemaReference referenceSchema = _defaultSchemaGenerator.GenerateSchema(ErrorObjectType, schemaRepository).AsReferenceSchema(); - traceScope.TraceSucceeded(referenceSchema.Reference.Id); + traceScope.TraceSucceeded(referenceSchema.GetReferenceId()); return referenceSchema; } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ResourceOrRelationshipDocumentSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ResourceOrRelationshipDocumentSchemaGenerator.cs index 0762e5b8c1..eff27e5eb8 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ResourceOrRelationshipDocumentSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/ResourceOrRelationshipDocumentSchemaGenerator.cs @@ -1,7 +1,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; using JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Documents; @@ -34,7 +34,7 @@ public override bool CanGenerate(Type schemaType) return JsonApiSchemaFacts.IsRequestDocumentSchemaType(schemaType) || JsonApiSchemaFacts.IsResponseDocumentSchemaType(schemaType); } - protected override OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) + protected override OpenApiSchemaReference GenerateDocumentSchema(Type schemaType, SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaType); ArgumentNullException.ThrowIfNull(schemaRepository); @@ -42,14 +42,17 @@ protected override OpenApiSchema GenerateDocumentSchema(Type schemaType, SchemaR var resourceSchemaType = ResourceSchemaType.Create(schemaType, _resourceGraph); bool isRequestSchema = JsonApiSchemaFacts.IsRequestDocumentSchemaType(resourceSchemaType.SchemaOpenType); - _ = _dataContainerSchemaGenerator.GenerateSchema(schemaType, resourceSchemaType.ResourceType, isRequestSchema, !isRequestSchema, schemaRepository); + _dataContainerSchemaGenerator.GenerateSchema(schemaType, resourceSchemaType.ResourceType, isRequestSchema, !isRequestSchema, schemaRepository); - OpenApiSchema? referenceSchemaForDocument = _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository); - OpenApiSchema inlineSchemaForDocument = schemaRepository.Schemas[referenceSchemaForDocument.Reference.Id].UnwrapLastExtendedSchema(); + OpenApiSchemaReference referenceSchemaForDocument = _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository).AsReferenceSchema(); + + OpenApiSchema inlineSchemaForDocument = + schemaRepository.Schemas[referenceSchemaForDocument.GetReferenceId()].UnwrapLastExtendedSchema().AsInlineSchema(); if (JsonApiSchemaFacts.HasNullableDataProperty(resourceSchemaType.SchemaOpenType)) { - inlineSchemaForDocument.Properties[JsonApiPropertyName.Data].Nullable = true; + inlineSchemaForDocument.Properties ??= new Dictionary(); + inlineSchemaForDocument.Properties[JsonApiPropertyName.Data].AsInlineSchema().SetNullable(true); } return referenceSchemaForDocument; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/GenerationCacheSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/GenerationCacheSchemaGenerator.cs index d67203979a..5750899a52 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/GenerationCacheSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/GenerationCacheSchemaGenerator.cs @@ -1,8 +1,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata.ActionMethods; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators; @@ -31,40 +30,40 @@ public bool HasAtomicOperationsEndpoint(SchemaRepository schemaRepository) { ArgumentNullException.ThrowIfNull(schemaRepository); - OpenApiSchema fullSchema = GenerateFullSchema(schemaRepository); + OpenApiSchema inlineSchema = GenerateInlineSchema(schemaRepository); - var hasAtomicOperationsEndpoint = (OpenApiBoolean)fullSchema.Properties[HasAtomicOperationsEndpointPropertyName].Default; - return hasAtomicOperationsEndpoint.Value; + return inlineSchema.Properties != null && + inlineSchema.Properties.TryGetValue(HasAtomicOperationsEndpointPropertyName, out IOpenApiSchema? propertyValue) && (bool)propertyValue.Default!; } - private OpenApiSchema GenerateFullSchema(SchemaRepository schemaRepository) + private OpenApiSchema GenerateInlineSchema(SchemaRepository schemaRepository) { - if (schemaRepository.Schemas.TryGetValue(SchemaId, out OpenApiSchema? fullSchema)) + if (schemaRepository.Schemas.TryGetValue(SchemaId, out IOpenApiSchema? existingSchema)) { - return fullSchema; + return existingSchema.AsInlineSchema(); } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this); bool hasAtomicOperationsEndpoint = EvaluateHasAtomicOperationsEndpoint(); - fullSchema = new OpenApiSchema + var inlineSchema = new OpenApiSchema { - Type = "object", - Properties = new Dictionary + Type = JsonSchemaType.Object, + Properties = new Dictionary { - [HasAtomicOperationsEndpointPropertyName] = new() + [HasAtomicOperationsEndpointPropertyName] = new OpenApiSchema { - Type = "boolean", - Default = new OpenApiBoolean(hasAtomicOperationsEndpoint) + Type = JsonSchemaType.Boolean, + Default = hasAtomicOperationsEndpoint } } }; - schemaRepository.AddDefinition(SchemaId, fullSchema); + schemaRepository.AddDefinition(SchemaId, inlineSchema); traceScope.TraceSucceeded(SchemaId); - return fullSchema; + return inlineSchema; } private bool EvaluateHasAtomicOperationsEndpoint() diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs index b8e1d9f053..a8516daa0b 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/JsonApiSchemaGenerator.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Documents; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators; @@ -26,7 +26,7 @@ public JsonApiSchemaGenerator(SchemaGenerator defaultSchemaGenerator, ResourceId _documentSchemaGenerators = documentSchemaGenerators as DocumentSchemaGenerator[] ?? documentSchemaGenerators.ToArray(); } - public OpenApiSchema GenerateSchema(Type schemaType, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null, + public IOpenApiSchema GenerateSchema(Type schemaType, SchemaRepository schemaRepository, MemberInfo? memberInfo = null, ParameterInfo? parameterInfo = null, ApiParameterRouteInfo? routeInfo = null) { ArgumentNullException.ThrowIfNull(schemaType); @@ -41,16 +41,16 @@ public OpenApiSchema GenerateSchema(Type schemaType, SchemaRepository schemaRepo if (schemaGenerator != null) { - OpenApiSchema referenceSchema = schemaGenerator.GenerateSchema(schemaType, schemaRepository); + IOpenApiSchema documentSchema = schemaGenerator.GenerateSchema(schemaType, schemaRepository); if (memberInfo != null || parameterInfo != null) { // For unknown reasons, Swashbuckle chooses to wrap request bodies in allOf, but not response bodies. // We just replicate that behavior here. See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/861#issuecomment-1373631712. - referenceSchema = referenceSchema.WrapInExtendedSchema(); + documentSchema = documentSchema.WrapInExtendedSchema(); } - return referenceSchema; + return documentSchema; } return _defaultSchemaGenerator.GenerateSchema(schemaType, schemaRepository, memberInfo, parameterInfo, routeInfo); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaRepositoryExtensions.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaRepositoryExtensions.cs index 669c50b4e0..f6f05fded3 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaRepositoryExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaRepositoryExtensions.cs @@ -1,5 +1,6 @@ +using System.Diagnostics.CodeAnalysis; using System.Reflection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; @@ -28,12 +29,12 @@ private static FieldInfo GetReservedIdsField() return field; } - public static OpenApiSchema LookupByType(this SchemaRepository schemaRepository, Type schemaType) + public static OpenApiSchemaReference LookupByType(this SchemaRepository schemaRepository, Type schemaType) { ArgumentNullException.ThrowIfNull(schemaRepository); ArgumentNullException.ThrowIfNull(schemaType); - if (!schemaRepository.TryLookupByType(schemaType, out OpenApiSchema? referenceSchema)) + if (!schemaRepository.TryLookupByTypeSafe(schemaType, out OpenApiSchemaReference? referenceSchema)) { throw new InvalidOperationException($"Reference schema for '{schemaType.Name}' does not exist."); } @@ -41,20 +42,27 @@ public static OpenApiSchema LookupByType(this SchemaRepository schemaRepository, return referenceSchema; } + public static bool TryLookupByTypeSafe(this SchemaRepository schemaRepository, Type type, [NotNullWhen(true)] out OpenApiSchemaReference? referenceSchema) + { + bool result = schemaRepository.TryLookupByType(type, out OpenApiSchemaReference? obliviousReferenceSchema); + referenceSchema = result ? obliviousReferenceSchema : null; + return result; + } + public static void ReplaceSchemaId(this SchemaRepository schemaRepository, Type oldSchemaType, string newSchemaId) { ArgumentNullException.ThrowIfNull(schemaRepository); ArgumentNullException.ThrowIfNull(oldSchemaType); ArgumentException.ThrowIfNullOrEmpty(newSchemaId); - if (schemaRepository.TryLookupByType(oldSchemaType, out OpenApiSchema? referenceSchema)) + if (schemaRepository.TryLookupByTypeSafe(oldSchemaType, out OpenApiSchemaReference? referenceSchema)) { - string oldSchemaId = referenceSchema.Reference.Id; + string oldSchemaId = referenceSchema.GetReferenceId(); - OpenApiSchema fullSchema = schemaRepository.Schemas[oldSchemaId]; + IOpenApiSchema targetSchema = schemaRepository.Schemas[oldSchemaId]; schemaRepository.Schemas.Remove(oldSchemaId); - schemaRepository.Schemas.Add(newSchemaId, fullSchema); + schemaRepository.Schemas.Add(newSchemaId, targetSchema); var reservedIds = (Dictionary)ReservedIdsField.GetValue(schemaRepository)!; reservedIds.Remove(oldSchemaType); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SetSchemaTypeToObjectDocumentFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SetSchemaTypeToObjectDocumentFilter.cs index 2764f868e6..da6672ed6a 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SetSchemaTypeToObjectDocumentFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SetSchemaTypeToObjectDocumentFilter.cs @@ -1,5 +1,5 @@ using JetBrains.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; @@ -11,12 +11,15 @@ internal sealed class SetSchemaTypeToObjectDocumentFilter : IDocumentFilter public void Apply(OpenApiDocument document, DocumentFilterContext context) { - foreach (OpenApiSchema schema in document.Components.Schemas.Values) + if (document.Components?.Schemas != null) { - if (schema.Extensions.ContainsKey(RequiresRootObjectTypeKey)) + foreach (OpenApiSchema inlineSchema in document.Components.Schemas.Values.OfType()) { - schema.Type = "object"; - schema.Extensions.Remove(RequiresRootObjectTypeKey); + if (inlineSchema.Extensions != null && inlineSchema.Extensions.ContainsKey(RequiresRootObjectTypeKey)) + { + inlineSchema.Type = JsonSchemaType.Object; + inlineSchema.Extensions.Remove(RequiresRootObjectTypeKey); + } } } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs index caeb40e96b..e5ccf92379 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/DocumentationOpenApiOperationFilter.cs @@ -5,12 +5,10 @@ using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Middleware; using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata.ActionMethods; -using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Net.Http.Headers; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; @@ -18,15 +16,15 @@ namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; [UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)] internal sealed class DocumentationOpenApiOperationFilter : IOperationFilter { - private const string GetPrimaryName = nameof(BaseJsonApiController, int>.GetAsync); - private const string GetSecondaryName = nameof(BaseJsonApiController, int>.GetSecondaryAsync); - private const string GetRelationshipName = nameof(BaseJsonApiController, int>.GetRelationshipAsync); - private const string PostResourceName = nameof(BaseJsonApiController, int>.PostAsync); - private const string PostRelationshipName = nameof(BaseJsonApiController, int>.PostRelationshipAsync); - private const string PatchResourceName = nameof(BaseJsonApiController, int>.PatchAsync); - private const string PatchRelationshipName = nameof(BaseJsonApiController, int>.PatchRelationshipAsync); - private const string DeleteResourceName = nameof(BaseJsonApiController, int>.DeleteAsync); - private const string DeleteRelationshipName = nameof(BaseJsonApiController, int>.DeleteRelationshipAsync); + private const string GetPrimaryName = nameof(BaseJsonApiController<,>.GetAsync); + private const string GetSecondaryName = nameof(BaseJsonApiController<,>.GetSecondaryAsync); + private const string GetRelationshipName = nameof(BaseJsonApiController<,>.GetRelationshipAsync); + private const string PostResourceName = nameof(BaseJsonApiController<,>.PostAsync); + private const string PostRelationshipName = nameof(BaseJsonApiController<,>.PostRelationshipAsync); + private const string PatchResourceName = nameof(BaseJsonApiController<,>.PatchAsync); + private const string PatchRelationshipName = nameof(BaseJsonApiController<,>.PatchRelationshipAsync); + private const string DeleteResourceName = nameof(BaseJsonApiController<,>.DeleteAsync); + private const string DeleteRelationshipName = nameof(BaseJsonApiController<,>.DeleteRelationshipAsync); private const string PostOperationsName = nameof(BaseJsonApiOperationsController.PostOperationsAsync); private const string TextCompareETag = @@ -76,11 +74,11 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) bool hasHeadVerb = context.ApiDescription.HttpMethod == "HEAD"; - if (hasHeadVerb) + if (hasHeadVerb && operation.Responses != null) { - foreach (OpenApiResponse response in operation.Responses.Values) + foreach (IOpenApiResponse response in operation.Responses.Values) { - response.Content.Clear(); + response.Content?.Clear(); } } @@ -171,7 +169,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) private static void ApplyGetPrimary(OpenApiOperation operation, ResourceType resourceType, bool hasHeadVerb) { - if (operation.Parameters.Count == 0) + if (operation.Parameters == null || operation.Parameters.Count == 0) { if (hasHeadVerb) { @@ -222,7 +220,7 @@ private static void ApplyGetPrimary(OpenApiOperation operation, ResourceType res SetResponseHeaderETag(operation.Responses, HttpStatusCode.NotModified); } - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularName} to retrieve."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularName} to retrieve."); AddQueryStringParameters(operation, false); AddRequestHeaderIfNoneMatch(operation); SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); @@ -265,7 +263,7 @@ private void ApplyPatchResource(OpenApiOperation operation, ResourceType resourc string singularName = resourceType.PublicName.Singularize(); SetOperationSummary(operation, $"Updates an existing {singularName}."); - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularName} to update."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularName} to update."); AddQueryStringParameters(operation, false); SetRequestBodyDescription(operation.RequestBody, @@ -288,7 +286,7 @@ private void ApplyDeleteResource(OpenApiOperation operation, ResourceType resour string singularName = resourceType.PublicName.Singularize(); SetOperationSummary(operation, $"Deletes an existing {singularName} by its identifier."); - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularName} to delete."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularName} to delete."); SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {singularName} was successfully deleted."); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularName} does not exist."); } @@ -326,7 +324,7 @@ relationship is HasOneAttribute SetResponseHeaderETag(operation.Responses, HttpStatusCode.NotModified); } - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} whose related {rightName} to retrieve."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularLeftName} whose related {rightName} to retrieve."); AddQueryStringParameters(operation, false); AddRequestHeaderIfNoneMatch(operation); SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); @@ -368,7 +366,7 @@ relationship is HasOneAttribute SetResponseHeaderETag(operation.Responses, HttpStatusCode.NotModified); } - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} whose related {singularRightName} {ident} to retrieve."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularLeftName} whose related {singularRightName} {ident} to retrieve."); AddQueryStringParameters(operation, true); AddRequestHeaderIfNoneMatch(operation); SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); @@ -381,7 +379,7 @@ private void ApplyPostRelationship(OpenApiOperation operation, RelationshipAttri string rightName = relationship.RightType.PublicName; SetOperationSummary(operation, $"Adds existing {rightName} to the {relationship} relationship of an individual {singularLeftName}."); - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} to add {rightName} to."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularLeftName} to add {rightName} to."); SetRequestBodyDescription(operation.RequestBody, $"The identities of the {rightName} to add to the {relationship} relationship."); SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, @@ -406,7 +404,7 @@ relationship is HasOneAttribute : $"Assigns an existing {rightName} to the {relationship} relationship of an individual {singularLeftName}." : $"Assigns existing {rightName} to the {relationship} relationship of an individual {singularLeftName}."); - SetParameterDescription(operation.Parameters[0], + SetParameterDescription(operation.Parameters, 0, isOptional ? $"The identifier of the {singularLeftName} whose {relationship} relationship to assign or clear." : $"The identifier of the {singularLeftName} whose {relationship} relationship to assign."); @@ -433,7 +431,7 @@ private void ApplyDeleteRelationship(OpenApiOperation operation, RelationshipAtt string rightName = relationship.RightType.PublicName; SetOperationSummary(operation, $"Removes existing {rightName} from the {relationship} relationship of an individual {singularLeftName}."); - SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} to remove {rightName} from."); + SetParameterDescription(operation.Parameters, 0, $"The identifier of the {singularLeftName} to remove {rightName} from."); SetRequestBodyDescription(operation.RequestBody, $"The identities of the {rightName} to remove from the {relationship} relationship."); SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, @@ -463,25 +461,35 @@ private static void SetOperationRemarks(OpenApiOperation operation, string descr operation.Description = XmlCommentsTextHelper.Humanize(description); } - private static void SetParameterDescription(OpenApiParameter parameter, string description) + private static void SetParameterDescription(IList? parameters, int index, string description) { + ConsistencyGuard.ThrowIf(parameters is null); + + IOpenApiParameter parameter = parameters[index]; parameter.Description = XmlCommentsTextHelper.Humanize(description); } - private static void SetRequestBodyDescription(OpenApiRequestBody requestBody, string description) + private static void SetRequestBodyDescription(IOpenApiRequestBody? requestBody, string description) { + ConsistencyGuard.ThrowIf(requestBody is null); + requestBody.Description = XmlCommentsTextHelper.Humanize(description); } - private static void SetResponseDescription(OpenApiResponses responses, HttpStatusCode statusCode, string description) + private static void SetResponseDescription(OpenApiResponses? responses, HttpStatusCode statusCode, string description) { + ConsistencyGuard.ThrowIf(responses is null); + OpenApiResponse response = GetOrAddResponse(responses, statusCode); response.Description = XmlCommentsTextHelper.Humanize(description); } - private static void SetResponseHeaderETag(OpenApiResponses responses, HttpStatusCode statusCode) + private static void SetResponseHeaderETag(OpenApiResponses? responses, HttpStatusCode statusCode) { + ConsistencyGuard.ThrowIf(responses is null); + OpenApiResponse response = GetOrAddResponse(responses, statusCode); + response.Headers ??= new SortedDictionary(StringComparer.Ordinal); response.Headers[HeaderNames.ETag] = new OpenApiHeader { @@ -489,14 +497,17 @@ private static void SetResponseHeaderETag(OpenApiResponses responses, HttpStatus Required = true, Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }; } - private static void SetResponseHeaderContentLength(OpenApiResponses responses, HttpStatusCode statusCode) + private static void SetResponseHeaderContentLength(OpenApiResponses? responses, HttpStatusCode statusCode) { + ConsistencyGuard.ThrowIf(responses is null); + OpenApiResponse response = GetOrAddResponse(responses, statusCode); + response.Headers ??= new SortedDictionary(StringComparer.Ordinal); response.Headers[HeaderNames.ContentLength] = new OpenApiHeader { @@ -504,15 +515,18 @@ private static void SetResponseHeaderContentLength(OpenApiResponses responses, H Required = true, Schema = new OpenApiSchema { - Type = "integer", + Type = JsonSchemaType.Integer, Format = "int64" } }; } - private static void SetResponseHeaderLocation(OpenApiResponses responses, HttpStatusCode statusCode, string resourceName) + private static void SetResponseHeaderLocation(OpenApiResponses? responses, HttpStatusCode statusCode, string resourceName) { + ConsistencyGuard.ThrowIf(responses is null); + OpenApiResponse response = GetOrAddResponse(responses, statusCode); + response.Headers ??= new SortedDictionary(StringComparer.Ordinal); response.Headers[HeaderNames.Location] = new OpenApiHeader { @@ -520,7 +534,7 @@ private static void SetResponseHeaderLocation(OpenApiResponses responses, HttpSt Required = true, Schema = new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, Format = "uri" } }; @@ -530,13 +544,13 @@ private static OpenApiResponse GetOrAddResponse(OpenApiResponses responses, Http { string responseCode = ((int)statusCode).ToString(); - if (!responses.TryGetValue(responseCode, out OpenApiResponse? response)) + if (!responses.TryGetValue(responseCode, out IOpenApiResponse? response) || response is not OpenApiResponse) { response = new OpenApiResponse(); responses.Add(responseCode, response); } - return response; + return (OpenApiResponse)response; } private static void AddQueryStringParameters(OpenApiOperation operation, bool isRelationshipEndpoint) @@ -551,20 +565,21 @@ private static void AddQueryStringParameters(OpenApiOperation operation, bool is // - This makes NSwag produce a C# client with method signature: GetAsync(IDictionary? query) // when combined with true in the project file. + operation.Parameters ??= new List(); + operation.Parameters.Add(new OpenApiParameter { In = ParameterLocation.Query, Name = "query", Schema = new OpenApiSchema { - Type = "object", + Type = JsonSchemaType.Object, AdditionalProperties = new OpenApiSchema { - Type = "string", - Nullable = true + Type = JsonSchemaType.String | JsonSchemaType.Null }, // Prevent SwaggerUI from producing sample, which fails when used because unknown query string parameters are blocked by default. - Example = new OpenApiString(string.Empty) + Example = string.Empty }, Description = isRelationshipEndpoint ? RelationshipQueryStringParameters : ResourceQueryStringParameters }); @@ -572,6 +587,8 @@ private static void AddQueryStringParameters(OpenApiOperation operation, bool is private static void AddRequestHeaderIfNoneMatch(OpenApiOperation operation) { + operation.Parameters ??= new List(); + operation.Parameters.Add(new OpenApiParameter { In = ParameterLocation.Header, @@ -579,7 +596,7 @@ private static void AddRequestHeaderIfNoneMatch(OpenApiOperation operation) Description = "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", Schema = new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String } }); } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/EndpointOrderingFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/EndpointOrderingFilter.cs index 047ba4355a..c1fcb5f582 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/EndpointOrderingFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/EndpointOrderingFilter.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using JetBrains.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; @@ -13,28 +13,43 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) ArgumentNullException.ThrowIfNull(swaggerDoc); ArgumentNullException.ThrowIfNull(context); - KeyValuePair[] endpointsInOrder = swaggerDoc.Paths.OrderBy(GetPrimaryResourcePublicName) + KeyValuePair[] endpointsInOrder = swaggerDoc.Paths.OrderBy(GetPrimaryResourcePublicName) .ThenBy(GetRelationshipName).ThenBy(IsSecondaryEndpoint).ToArray(); - swaggerDoc.Paths.Clear(); + swaggerDoc.Paths = new OpenApiPaths(); - foreach ((string url, OpenApiPathItem path) in endpointsInOrder) + foreach ((string url, IOpenApiPathItem path) in endpointsInOrder) { swaggerDoc.Paths.Add(url, path); } } - private static string GetPrimaryResourcePublicName(KeyValuePair entry) + private static string GetPrimaryResourcePublicName(KeyValuePair entry) { - return entry.Value.Operations.First().Value.Tags.First().Name; + if (entry.Value.Operations is { Count: > 0 }) + { + ISet? tagReferences = entry.Value.Operations.First().Value.Tags; + + if (tagReferences is { Count: > 0 }) + { + OpenApiTagReference openApiTagReference = tagReferences.First(); + + if (openApiTagReference.Name != null) + { + return openApiTagReference.Name; + } + } + } + + throw new InvalidOperationException($"Failed to find tag value for endpoint '{entry.Key}'."); } - private static bool IsSecondaryEndpoint(KeyValuePair entry) + private static bool IsSecondaryEndpoint(KeyValuePair entry) { return entry.Key.Contains("/relationships"); } - private static string GetRelationshipName(KeyValuePair entry) + private static string GetRelationshipName(KeyValuePair entry) { Match match = RelationshipNameInUrlRegex().Match(entry.Key); diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/JsonApiDataContractResolver.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/JsonApiDataContractResolver.cs index dcc652c696..c4e6fbfd39 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/JsonApiDataContractResolver.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/JsonApiDataContractResolver.cs @@ -63,7 +63,7 @@ private List GetDataPropertiesThatExistInResourceClrType(Type reso foreach (DataProperty property in dataContract.ObjectProperties) { - if (property.MemberInfo.Name == nameof(Identifiable.Id)) + if (property.MemberInfo.Name == nameof(Identifiable<>.Id)) { // Schemas of JsonApiDotNetCore resources will obtain an Id property through inheritance of a resource identifier type. continue; diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs index 31bf7be625..5b0350dba9 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ResourceFieldSchemaBuilder.cs @@ -3,7 +3,7 @@ using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.ResourceObjects; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators.Components; using JsonApiDotNetCore.Resources.Annotations; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; @@ -20,7 +20,7 @@ internal sealed class ResourceFieldSchemaBuilder private readonly SchemaRepository _resourceSchemaRepository = new(); private readonly ResourceDocumentationReader _resourceDocumentationReader = new(); - private readonly IDictionary _schemasForResourceFields; + private readonly IDictionary _schemasForResourceFields; public ResourceFieldSchemaBuilder(SchemaGenerationTracer schemaGenerationTracer, SchemaGenerator defaultSchemaGenerator, DataSchemaGenerator dataSchemaGenerator, LinksVisibilitySchemaGenerator linksVisibilitySchemaGenerator, @@ -46,26 +46,29 @@ public ResourceFieldSchemaBuilder(SchemaGenerationTracer schemaGenerationTracer, _schemasForResourceFields = GetFieldSchemas(); } - private IDictionary GetFieldSchemas() + private IDictionary GetFieldSchemas() { - if (!_resourceSchemaRepository.TryLookupByType(_resourceSchemaType.ResourceType.ClrType, out OpenApiSchema referenceSchemaForResource)) + if (!_resourceSchemaRepository.TryLookupByTypeSafe(_resourceSchemaType.ResourceType.ClrType, out OpenApiSchemaReference? referenceSchemaForResource)) { - referenceSchemaForResource = _defaultSchemaGenerator.GenerateSchema(_resourceSchemaType.ResourceType.ClrType, _resourceSchemaRepository); + referenceSchemaForResource = _defaultSchemaGenerator.GenerateSchema(_resourceSchemaType.ResourceType.ClrType, _resourceSchemaRepository) + .AsReferenceSchema(); } - OpenApiSchema inlineSchemaForResource = _resourceSchemaRepository.Schemas[referenceSchemaForResource.Reference.Id].UnwrapLastExtendedSchema(); - return inlineSchemaForResource.Properties; + OpenApiSchema inlineSchemaForResource = + _resourceSchemaRepository.Schemas[referenceSchemaForResource.GetReferenceId()].UnwrapLastExtendedSchema().AsInlineSchema(); + + return inlineSchemaForResource.Properties ?? new Dictionary(); } - public void SetMembersOfAttributes(OpenApiSchema fullSchemaForAttributes, bool forRequestSchema, SchemaRepository schemaRepository) + public void SetMembersOfAttributes(OpenApiSchema inlineSchemaForAttributes, bool forRequestSchema, SchemaRepository schemaRepository) { - ArgumentNullException.ThrowIfNull(fullSchemaForAttributes); + ArgumentNullException.ThrowIfNull(inlineSchemaForAttributes); ArgumentNullException.ThrowIfNull(schemaRepository); - AssertHasNoProperties(fullSchemaForAttributes); + AssertHasNoProperties(inlineSchemaForAttributes); AttrCapabilities requiredCapability = GetRequiredCapabilityForAttributes(_resourceSchemaType.SchemaOpenType); - foreach ((string publicName, OpenApiSchema schemaForResourceField) in _schemasForResourceFields) + foreach ((string publicName, IOpenApiSchema schemaForResourceField) in _schemasForResourceFields) { AttrAttribute? matchingAttribute = _resourceSchemaType.ResourceType.FindAttributeByPublicName(publicName); @@ -86,22 +89,28 @@ public void SetMembersOfAttributes(OpenApiSchema fullSchemaForAttributes, bool f } } - bool isInlineSchemaType = schemaForResourceField.AllOf.Count == 0; + bool isInlineSchemaType = schemaForResourceField.AllOf == null || schemaForResourceField.AllOf.Count == 0; // Schemas for types like enum and complex attributes are handled as reference schemas. if (!isInlineSchemaType) { - OpenApiSchema referenceSchemaForAttribute = schemaForResourceField.UnwrapLastExtendedSchema(); + OpenApiSchemaReference referenceSchemaForAttribute = schemaForResourceField.UnwrapLastExtendedSchema().AsReferenceSchema(); EnsureAttributeSchemaIsExposed(referenceSchemaForAttribute, matchingAttribute, schemaRepository); } - fullSchemaForAttributes.Properties.Add(matchingAttribute.PublicName, schemaForResourceField); + inlineSchemaForAttributes.Properties ??= new Dictionary(); + inlineSchemaForAttributes.Properties.Add(matchingAttribute.PublicName, schemaForResourceField); - schemaForResourceField.Nullable = _resourceFieldValidationMetadataProvider.IsNullable(matchingAttribute); + if (schemaForResourceField is OpenApiSchema inlineSchemaForResourceField) + { + bool isNullable = _resourceFieldValidationMetadataProvider.IsNullable(matchingAttribute); + inlineSchemaForResourceField.SetNullable(isNullable); + } if (IsFieldRequired(matchingAttribute)) { - fullSchemaForAttributes.Required.Add(matchingAttribute.PublicName); + inlineSchemaForAttributes.Required ??= new SortedSet(StringComparer.Ordinal); + inlineSchemaForAttributes.Required.Add(matchingAttribute.PublicName); } schemaForResourceField.Description = _resourceDocumentationReader.GetDocumentationForAttribute(matchingAttribute); @@ -130,21 +139,21 @@ private static AttrCapabilities GetRequiredCapabilityForAttributes(Type resource return capabilities.Value; } - private void EnsureAttributeSchemaIsExposed(OpenApiSchema referenceSchemaForAttribute, AttrAttribute attribute, SchemaRepository schemaRepository) + private void EnsureAttributeSchemaIsExposed(OpenApiSchemaReference referenceSchemaForAttribute, AttrAttribute attribute, SchemaRepository schemaRepository) { Type nonNullableTypeInPropertyType = GetRepresentedTypeForAttributeSchema(attribute); - if (schemaRepository.TryLookupByType(nonNullableTypeInPropertyType, out _)) + if (schemaRepository.TryLookupByTypeSafe(nonNullableTypeInPropertyType, out _)) { return; } using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, nonNullableTypeInPropertyType); - string schemaId = referenceSchemaForAttribute.Reference.Id; - OpenApiSchema fullSchema = _resourceSchemaRepository.Schemas[schemaId]; + string schemaId = referenceSchemaForAttribute.GetReferenceId(); + OpenApiSchema inlineSchema = _resourceSchemaRepository.Schemas[schemaId].AsInlineSchema(); - schemaRepository.AddDefinition(schemaId, fullSchema); + schemaRepository.AddDefinition(schemaId, inlineSchema); schemaRepository.RegisterType(nonNullableTypeInPropertyType, schemaId); traceScope.TraceSucceeded(schemaId); @@ -170,11 +179,11 @@ private bool IsFieldRequired(ResourceFieldAttribute field) return isCreateResourceSchemaType && _resourceFieldValidationMetadataProvider.IsRequired(field); } - public void SetMembersOfRelationships(OpenApiSchema fullSchemaForRelationships, bool forRequestSchema, SchemaRepository schemaRepository) + public void SetMembersOfRelationships(OpenApiSchema inlineSchemaForRelationships, bool forRequestSchema, SchemaRepository schemaRepository) { - ArgumentNullException.ThrowIfNull(fullSchemaForRelationships); + ArgumentNullException.ThrowIfNull(inlineSchemaForRelationships); ArgumentNullException.ThrowIfNull(schemaRepository); - AssertHasNoProperties(fullSchemaForRelationships); + AssertHasNoProperties(inlineSchemaForRelationships); foreach (string publicName in _schemasForResourceFields.Keys) { @@ -186,27 +195,29 @@ public void SetMembersOfRelationships(OpenApiSchema fullSchemaForRelationships, Type identifierSchemaConstructedType = identifierSchemaOpenType.MakeGenericType(matchingRelationship.RightType.ClrType); _ = _dataSchemaGenerator.GenerateSchema(identifierSchemaConstructedType, forRequestSchema, schemaRepository); - AddRelationshipSchemaToResourceData(matchingRelationship, fullSchemaForRelationships, schemaRepository); + AddRelationshipSchemaToResourceData(matchingRelationship, inlineSchemaForRelationships, schemaRepository); } } } - private void AddRelationshipSchemaToResourceData(RelationshipAttribute relationship, OpenApiSchema fullSchemaForRelationships, + private void AddRelationshipSchemaToResourceData(RelationshipAttribute relationship, OpenApiSchema inlineSchemaForRelationships, SchemaRepository schemaRepository) { Type relationshipSchemaType = GetRelationshipSchemaType(relationship, _resourceSchemaType.SchemaOpenType); - OpenApiSchema referenceSchemaForRelationship = GetReferenceSchemaForRelationship(relationshipSchemaType, schemaRepository) ?? + OpenApiSchemaReference referenceSchemaForRelationship = GetReferenceSchemaForRelationship(relationshipSchemaType, schemaRepository) ?? CreateReferenceSchemaForRelationship(relationshipSchemaType, schemaRepository); OpenApiSchema extendedReferenceSchemaForRelationship = referenceSchemaForRelationship.WrapInExtendedSchema(); extendedReferenceSchemaForRelationship.Description = _resourceDocumentationReader.GetDocumentationForRelationship(relationship); - fullSchemaForRelationships.Properties.Add(relationship.PublicName, extendedReferenceSchemaForRelationship); + inlineSchemaForRelationships.Properties ??= new Dictionary(); + inlineSchemaForRelationships.Properties.Add(relationship.PublicName, extendedReferenceSchemaForRelationship); if (IsFieldRequired(relationship)) { - fullSchemaForRelationships.Required.Add(relationship.PublicName); + inlineSchemaForRelationships.Required ??= new SortedSet(StringComparer.Ordinal); + inlineSchemaForRelationships.Required.Add(relationship.PublicName); } } @@ -216,35 +227,35 @@ private Type GetRelationshipSchemaType(RelationshipAttribute relationship, Type return isResponseSchemaType ? _relationshipTypeFactory.GetForResponse(relationship) : _relationshipTypeFactory.GetForRequest(relationship); } - private OpenApiSchema? GetReferenceSchemaForRelationship(Type relationshipSchemaType, SchemaRepository schemaRepository) + private OpenApiSchemaReference? GetReferenceSchemaForRelationship(Type relationshipSchemaType, SchemaRepository schemaRepository) { - return schemaRepository.TryLookupByType(relationshipSchemaType, out OpenApiSchema? referenceSchema) ? referenceSchema : null; + return schemaRepository.TryLookupByTypeSafe(relationshipSchemaType, out OpenApiSchemaReference? referenceSchema) ? referenceSchema : null; } - private OpenApiSchema CreateReferenceSchemaForRelationship(Type relationshipSchemaType, SchemaRepository schemaRepository) + private OpenApiSchemaReference CreateReferenceSchemaForRelationship(Type relationshipSchemaType, SchemaRepository schemaRepository) { using ISchemaGenerationTraceScope traceScope = _schemaGenerationTracer.TraceStart(this, relationshipSchemaType); - OpenApiSchema referenceSchema = _defaultSchemaGenerator.GenerateSchema(relationshipSchemaType, schemaRepository); - - OpenApiSchema fullSchema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + OpenApiSchemaReference referenceSchema = _defaultSchemaGenerator.GenerateSchema(relationshipSchemaType, schemaRepository).AsReferenceSchema(); + OpenApiSchema inlineSchema = schemaRepository.Schemas[referenceSchema.GetReferenceId()].AsInlineSchema(); if (JsonApiSchemaFacts.HasNullableDataProperty(relationshipSchemaType)) { - fullSchema.Properties[JsonApiPropertyName.Data].Nullable = true; + inlineSchema.Properties ??= new Dictionary(); + inlineSchema.Properties[JsonApiPropertyName.Data].AsInlineSchema().SetNullable(true); } if (JsonApiSchemaFacts.IsRelationshipInResponseType(relationshipSchemaType)) { - _linksVisibilitySchemaGenerator.UpdateSchemaForRelationship(relationshipSchemaType, fullSchema, schemaRepository); + _linksVisibilitySchemaGenerator.UpdateSchemaForRelationship(relationshipSchemaType, inlineSchema, schemaRepository); } - traceScope.TraceSucceeded(referenceSchema.Reference.Id); + traceScope.TraceSucceeded(referenceSchema.GetReferenceId()); return referenceSchema; } - private static void AssertHasNoProperties(OpenApiSchema fullSchema) + private static void AssertHasNoProperties(OpenApiSchema inlineSchema) { - ConsistencyGuard.ThrowIf(fullSchema.Properties.Count > 0); + ConsistencyGuard.ThrowIf(inlineSchema.Properties is { Count: > 0 }); } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ServerDocumentFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ServerDocumentFilter.cs index 723499d39c..2e2fab43a1 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ServerDocumentFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/ServerDocumentFilter.cs @@ -1,6 +1,6 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.Http; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; @@ -19,14 +19,19 @@ public ServerDocumentFilter(IHttpContextAccessor httpContextAccessor) public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - if (swaggerDoc.Servers.Count == 0 && _httpContextAccessor.HttpContext != null) + if (_httpContextAccessor.HttpContext != null) { - HttpRequest httpRequest = _httpContextAccessor.HttpContext.Request; + swaggerDoc.Servers ??= new List(); - swaggerDoc.Servers.Add(new OpenApiServer + if (swaggerDoc.Servers.Count == 0) { - Url = $"{httpRequest.Scheme}://{httpRequest.Host.Value}" - }); + HttpRequest httpRequest = _httpContextAccessor.HttpContext.Request; + + swaggerDoc.Servers.Add(new OpenApiServer + { + Url = $"{httpRequest.Scheme}://{httpRequest.Host.Value}" + }); + } } } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs index dec10db97b..3dfa5902f3 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/SwaggerComponents/StringEnumOrderingFilter.cs @@ -1,8 +1,7 @@ +using System.Text.Json; +using System.Text.Json.Nodes; using JetBrains.Annotations; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Services; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle.SwaggerComponents; @@ -24,31 +23,32 @@ public void Apply(OpenApiDocument document, DocumentFilterContext context) private sealed class OpenApiEnumVisitor : OpenApiVisitorBase { - public override void Visit(OpenApiSchema schema) + public override void Visit(IOpenApiSchema schema) { - if (HasSortAnnotation(schema)) + if (schema is OpenApiSchema { Extensions: not null } inlineSchema && HasSortAnnotation(inlineSchema.Extensions)) { - if (schema.Enum.Count > 1) - { - OrderEnumMembers(schema); - } + OrderEnumMembers(inlineSchema); } - schema.Extensions.Remove(RequiresSortKey); + schema.Extensions?.Remove(RequiresSortKey); } - private static bool HasSortAnnotation(OpenApiSchema schema) + private static bool HasSortAnnotation(IDictionary schemaExtensions) { // Order our own enums, but don't touch enums from user-defined resource attributes. - return schema.Extensions.TryGetValue(RequiresSortKey, out IOpenApiExtension? extension) && extension is OpenApiBoolean { Value: true }; + return schemaExtensions.TryGetValue(RequiresSortKey, out IOpenApiExtension? extension) && + extension is JsonNodeExtension { Node: JsonValue value } && value.GetValueKind() == JsonValueKind.True; } - private static void OrderEnumMembers(OpenApiSchema schema) + private static void OrderEnumMembers(OpenApiSchema inlineSchema) { - List ordered = schema.Enum.OfType().OrderBy(openApiString => openApiString.Value).Cast().ToList(); - ConsistencyGuard.ThrowIf(ordered.Count != schema.Enum.Count); + if (inlineSchema.Enum is { Count: > 1 }) + { + List ordered = inlineSchema.Enum.OrderBy(node => node.ToString()).ToList(); + ConsistencyGuard.ThrowIf(ordered.Count != inlineSchema.Enum.Count); - schema.Enum = ordered; + inlineSchema.Enum = ordered; + } } } } diff --git a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/UnusedComponentSchemaCleaner.cs b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/UnusedComponentSchemaCleaner.cs index 4e2addabb3..e59829ef94 100644 --- a/src/JsonApiDotNetCore.OpenApi.Swashbuckle/UnusedComponentSchemaCleaner.cs +++ b/src/JsonApiDotNetCore.OpenApi.Swashbuckle/UnusedComponentSchemaCleaner.cs @@ -1,9 +1,7 @@ using System.Diagnostics; using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.Swashbuckle.SchemaGenerators; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Services; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace JsonApiDotNetCore.OpenApi.Swashbuckle; @@ -21,19 +19,22 @@ public void Apply(OpenApiDocument document, DocumentFilterContext context) ArgumentNullException.ThrowIfNull(document); ArgumentNullException.ThrowIfNull(context); - document.Components.Schemas.Remove(GenerationCacheSchemaGenerator.SchemaId); + if (document.Components?.Schemas != null) + { + document.Components.Schemas.Remove(GenerationCacheSchemaGenerator.SchemaId); - HashSet unusedSchemaIds = GetUnusedSchemaIds(document); - AssertNoUnknownSchemasFound(unusedSchemaIds); + HashSet unusedSchemaIds = GetUnusedSchemaIds(document, document.Components.Schemas); + AssertNoUnknownSchemasFound(unusedSchemaIds); - RemoveUnusedComponentSchemas(document, unusedSchemaIds); + RemoveUnusedComponentSchemas(document.Components.Schemas, unusedSchemaIds); + } } - private static HashSet GetUnusedSchemaIds(OpenApiDocument document) + private static HashSet GetUnusedSchemaIds(OpenApiDocument document, IDictionary componentSchemas) { HashSet reachableSchemaIds = ReachableRootsCollector.Instance.CollectReachableSchemaIds(document); - ComponentSchemaUsageCollector collector = new(document); + ComponentSchemaUsageCollector collector = new(componentSchemas); return collector.CollectUnusedSchemaIds(reachableSchemaIds); } @@ -46,11 +47,11 @@ private static void AssertNoUnknownSchemasFound(HashSet unusedSchemaIds) } } - private static void RemoveUnusedComponentSchemas(OpenApiDocument document, HashSet unusedSchemaIds) + private static void RemoveUnusedComponentSchemas(IDictionary componentSchemas, HashSet unusedSchemaIds) { foreach (string schemaId in unusedSchemaIds) { - document.Components.Schemas.Remove(schemaId); + componentSchemas.Remove(schemaId); } } @@ -78,13 +79,13 @@ private sealed class ComponentSchemaReferenceVisitor : OpenApiVisitorBase { public HashSet ReachableSchemaIds { get; } = []; - public override void Visit(IOpenApiReferenceable referenceable) + public override void Visit(IOpenApiReferenceHolder referenceHolder) { if (!PathString.StartsWith(ComponentSchemaPrefix, StringComparison.Ordinal)) { - if (referenceable is OpenApiSchema schema) + if (referenceHolder is OpenApiSchemaReference { Reference.Id: not null } referenceSchema) { - ReachableSchemaIds.Add(schema.Reference.Id); + ReachableSchemaIds.Add(referenceSchema.Reference.Id); } } } @@ -93,14 +94,14 @@ public override void Visit(IOpenApiReferenceable referenceable) private sealed class ComponentSchemaUsageCollector { - private readonly IDictionary _componentSchemas; + private readonly IDictionary _componentSchemas; private readonly HashSet _schemaIdsInUse = []; - public ComponentSchemaUsageCollector(OpenApiDocument document) + public ComponentSchemaUsageCollector(IDictionary componentSchemas) { - ArgumentNullException.ThrowIfNull(document); + ArgumentNullException.ThrowIfNull(componentSchemas); - _componentSchemas = document.Components.Schemas; + _componentSchemas = componentSchemas; } public HashSet CollectUnusedSchemaIds(ICollection reachableSchemaIds) @@ -117,18 +118,18 @@ public HashSet CollectUnusedSchemaIds(ICollection reachableSchem return unusedSchemaIds; } - private void WalkSchemaId(string schemaId) + private void WalkSchemaId(string? schemaId) { - if (_schemaIdsInUse.Add(schemaId)) + if (schemaId != null && _schemaIdsInUse.Add(schemaId)) { - if (_componentSchemas.TryGetValue(schemaId, out OpenApiSchema? schema)) + if (_componentSchemas.TryGetValue(schemaId, out IOpenApiSchema? schema)) { WalkSchema(schema); } } } - private void WalkSchema(OpenApiSchema? schema) + private void WalkSchema(IOpenApiSchema? schema) { if (schema != null) { @@ -137,22 +138,22 @@ private void WalkSchema(OpenApiSchema? schema) WalkSchema(schema.Items); WalkSchema(schema.Not); - foreach (OpenApiSchema? subSchema in schema.AllOf) + foreach (IOpenApiSchema subSchema in schema.AllOf ?? Array.Empty()) { WalkSchema(subSchema); } - foreach (OpenApiSchema? subSchema in schema.AnyOf) + foreach (IOpenApiSchema subSchema in schema.AnyOf ?? Array.Empty()) { WalkSchema(subSchema); } - foreach (OpenApiSchema? subSchema in schema.OneOf) + foreach (IOpenApiSchema subSchema in schema.OneOf ?? Array.Empty()) { WalkSchema(subSchema); } - foreach (OpenApiSchema? subSchema in schema.Properties.Values) + foreach (IOpenApiSchema subSchema in schema.Properties?.Values ?? Array.Empty()) { WalkSchema(subSchema); } @@ -162,22 +163,18 @@ private void WalkSchema(OpenApiSchema? schema) } } - private void VisitSchema(OpenApiSchema schema) + private void VisitSchema(IOpenApiSchema schema) { - if (schema.Reference is { Type: ReferenceType.Schema, IsExternal: false }) + if (schema is OpenApiSchemaReference { Reference: { Type: ReferenceType.Schema, IsExternal: false } } referenceSchema) { - WalkSchemaId(schema.Reference.Id); + WalkSchemaId(referenceSchema.Reference.Id); } - if (schema.Discriminator != null) + if (schema.Discriminator is { Mapping: not null }) { - foreach (string mappingValue in schema.Discriminator.Mapping.Values) + foreach (OpenApiSchemaReference mappingValueReferenceSchema in schema.Discriminator.Mapping.Values) { - if (mappingValue.StartsWith(ComponentSchemaPrefix, StringComparison.Ordinal)) - { - string schemaId = mappingValue[ComponentSchemaPrefix.Length..]; - WalkSchemaId(schemaId); - } + WalkSchemaId(mappingValueReferenceSchema.Reference.Id); } } } diff --git a/src/JsonApiDotNetCore.SourceGenerators/FullControllerInfo.cs b/src/JsonApiDotNetCore.SourceGenerators/FullControllerInfo.cs index c11c44b4c5..1a75e0bf58 100644 --- a/src/JsonApiDotNetCore.SourceGenerators/FullControllerInfo.cs +++ b/src/JsonApiDotNetCore.SourceGenerators/FullControllerInfo.cs @@ -22,8 +22,6 @@ public static FullControllerInfo Create(CoreControllerInfo coreController, strin public FullControllerInfo WithHintFileName(string hintFileName) { - // ReSharper disable once UseWithExpressionToCopyRecord - // Justification: Workaround for bug at https://youtrack.jetbrains.com/issue/RSRP-502017/Invalid-suggestion-to-use-with-expression. return new FullControllerInfo(CoreController, ControllerType, LoggerFactoryInterface, hintFileName); } } diff --git a/src/JsonApiDotNetCore/AtomicOperations/DefaultOperationFilter.cs b/src/JsonApiDotNetCore/AtomicOperations/DefaultOperationFilter.cs index fb632dd276..cb139180ad 100644 --- a/src/JsonApiDotNetCore/AtomicOperations/DefaultOperationFilter.cs +++ b/src/JsonApiDotNetCore/AtomicOperations/DefaultOperationFilter.cs @@ -6,7 +6,7 @@ namespace JsonApiDotNetCore.AtomicOperations; -/// +/// public class DefaultOperationFilter : IAtomicOperationFilter { /// diff --git a/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs index 30b850e8b8..8d7a4a29e5 100644 --- a/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs +++ b/src/JsonApiDotNetCore/Configuration/JsonApiValidationFilter.cs @@ -61,7 +61,7 @@ private IServiceProvider GetScopedServiceProvider() private static bool IsId(string key) { - return key == nameof(Identifiable.Id) || key.EndsWith($".{nameof(Identifiable.Id)}", StringComparison.Ordinal); + return key == nameof(Identifiable<>.Id) || key.EndsWith($".{nameof(Identifiable<>.Id)}", StringComparison.Ordinal); } private static bool IsAtPrimaryEndpoint(IJsonApiRequest request) diff --git a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs index a10d6a904c..cabc8bcc06 100644 --- a/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs +++ b/src/JsonApiDotNetCore/Configuration/ResourceGraphBuilder.cs @@ -299,7 +299,7 @@ private Dictionary.ValueCollection GetAttributes(Type res if (attribute == null) { - if (property.Name == nameof(Identifiable.Id)) + if (property.Name == nameof(Identifiable<>.Id)) { // Although strictly not correct, 'id' is added to the list of attributes for convenience. // For example, it enables to filter on ID, without the need to special-case existing logic. diff --git a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs index fc85156e57..2c46313e34 100644 --- a/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs +++ b/src/JsonApiDotNetCore/Errors/InvalidModelStateException.cs @@ -134,7 +134,7 @@ private static List FromModelStateDictionary(IReadOnlyDictionary.Id) + string sourcePointer = attribute.Property.Name == nameof(Identifiable<>.Id) ? $"{segment.SourcePointer ?? "/data"}/{attribute.PublicName}" : $"{segment.SourcePointer ?? "/data"}/attributes/{attribute.PublicName}"; diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index d36600e878..bcc77754b2 100644 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -1,6 +1,6 @@ - net8.0 + net10.0;net8.0 true true @@ -40,6 +40,12 @@ + + + diff --git a/src/JsonApiDotNetCore/Middleware/JsonApiContentNegotiator.cs b/src/JsonApiDotNetCore/Middleware/JsonApiContentNegotiator.cs index a9806129f8..97bd528c90 100644 --- a/src/JsonApiDotNetCore/Middleware/JsonApiContentNegotiator.cs +++ b/src/JsonApiDotNetCore/Middleware/JsonApiContentNegotiator.cs @@ -8,7 +8,7 @@ namespace JsonApiDotNetCore.Middleware; -/// +/// [PublicAPI] public class JsonApiContentNegotiator : IJsonApiContentNegotiator { diff --git a/src/JsonApiDotNetCore/Queries/Parsing/FilterParser.cs b/src/JsonApiDotNetCore/Queries/Parsing/FilterParser.cs index 3babdae45e..34f88f1c77 100644 --- a/src/JsonApiDotNetCore/Queries/Parsing/FilterParser.cs +++ b/src/JsonApiDotNetCore/Queries/Parsing/FilterParser.cs @@ -540,7 +540,7 @@ protected virtual ConstantValueConverter GetConstantValueConverterForType(Type d private ConstantValueConverter GetConstantValueConverterForAttribute(AttrAttribute attribute) { - if (attribute is { Property.Name: nameof(Identifiable.Id) }) + if (attribute is { Property.Name: nameof(Identifiable<>.Id) }) { return (stringValue, position) => { diff --git a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs index 020040e928..c8fe4c5495 100644 --- a/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs +++ b/src/JsonApiDotNetCore/Queries/QueryLayerComposer.cs @@ -620,6 +620,6 @@ protected virtual PaginationExpression GetPagination(IReadOnlyCollection.Id)); + return resourceType.GetAttributeByPropertyName(nameof(Identifiable<>.Id)); } } diff --git a/src/JsonApiDotNetCore/Queries/SparseFieldSetCache.cs b/src/JsonApiDotNetCore/Queries/SparseFieldSetCache.cs index 0df2d5af75..f2c5593ab1 100644 --- a/src/JsonApiDotNetCore/Queries/SparseFieldSetCache.cs +++ b/src/JsonApiDotNetCore/Queries/SparseFieldSetCache.cs @@ -94,7 +94,7 @@ public IImmutableSet GetIdAttributeSetForRelationshipQuery(Resour { ArgumentNullException.ThrowIfNull(resourceType); - AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id)); + AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable<>.Id)); var inputExpression = new SparseFieldSetExpression(ImmutableHashSet.Create(idAttribute)); // Intentionally not cached, as we are fetching ID only (ignoring any sparse fieldset that came from query string). diff --git a/src/JsonApiDotNetCore/Queries/SystemExpressionBuilder.cs b/src/JsonApiDotNetCore/Queries/SystemExpressionBuilder.cs index 72a30a060a..bc1425b02e 100644 --- a/src/JsonApiDotNetCore/Queries/SystemExpressionBuilder.cs +++ b/src/JsonApiDotNetCore/Queries/SystemExpressionBuilder.cs @@ -1,7 +1,7 @@ using System.Linq.Expressions; using System.Reflection; -#pragma warning disable AV1008 +#pragma warning disable AV1008 // Class should not be static namespace JsonApiDotNetCore.Queries; diff --git a/src/JsonApiDotNetCore/QueryStrings/QueryStringReader.cs b/src/JsonApiDotNetCore/QueryStrings/QueryStringReader.cs index 89337708e7..8e58d1a1ff 100644 --- a/src/JsonApiDotNetCore/QueryStrings/QueryStringReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/QueryStringReader.cs @@ -47,7 +47,11 @@ public void ReadAll(DisableQueryStringAttribute? disableQueryStringAttribute) if (reader != null) { - LogParameterAccepted(parameterName, parameterValue, reader.GetType().Name); + if (_logger.IsEnabled(LogLevel.Debug)) + { + string readerType = reader.GetType().Name; + LogParameterAccepted(parameterName, parameterValue, readerType); + } if (!reader.AllowEmptyValue && string.IsNullOrEmpty(parameterValue)) { @@ -74,7 +78,8 @@ public void ReadAll(DisableQueryStringAttribute? disableQueryStringAttribute) } } - [LoggerMessage(Level = LogLevel.Debug, Message = "Query string parameter '{ParameterName}' with value '{ParameterValue}' was accepted by {ReaderType}.")] + [LoggerMessage(Level = LogLevel.Debug, SkipEnabledCheck = true, + Message = "Query string parameter '{ParameterName}' with value '{ParameterValue}' was accepted by {ReaderType}.")] private partial void LogParameterAccepted(string parameterName, StringValues parameterValue, string readerType); [LoggerMessage(Level = LogLevel.Debug, Message = "Query string parameter '{ParameterName}' was successfully read.")] diff --git a/src/JsonApiDotNetCore/QueryStrings/SparseFieldSetQueryStringParameterReader.cs b/src/JsonApiDotNetCore/QueryStrings/SparseFieldSetQueryStringParameterReader.cs index 308fe44588..454fb34c49 100644 --- a/src/JsonApiDotNetCore/QueryStrings/SparseFieldSetQueryStringParameterReader.cs +++ b/src/JsonApiDotNetCore/QueryStrings/SparseFieldSetQueryStringParameterReader.cs @@ -85,7 +85,7 @@ private SparseFieldSetExpression GetSparseFieldSet(string parameterValue, Resour if (sparseFieldSet == null) { // We add ID to an incoming empty fieldset, so that callers can distinguish between no fieldset and an empty one. - AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id)); + AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable<>.Id)); return new SparseFieldSetExpression(ImmutableHashSet.Create(idAttribute)); } diff --git a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs index f4c9af37c0..22bad02ef6 100644 --- a/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs +++ b/src/JsonApiDotNetCore/Repositories/EntityFrameworkCoreRepository.cs @@ -146,9 +146,9 @@ protected virtual IQueryable ApplyQueryLayer(QueryLayer queryLayer) source = queryableHandler.Apply(source); } -#pragma warning disable CS0618 +#pragma warning disable CS0618 // Type or member is obsolete IQueryableBuilder builder = _resourceDefinitionAccessor.QueryableBuilder; -#pragma warning restore CS0618 +#pragma warning restore CS0618 // Type or member is obsolete var context = QueryableBuilderContext.CreateRoot(source, typeof(Queryable), _dbContext.Model, null); Expression expression = builder.ApplyQuery(queryLayer, context); @@ -179,9 +179,9 @@ protected virtual IQueryable GetAll() { // EF Core rejects the way we project sparse fieldsets when owned entities are involved, unless the query is explicitly // marked as non-tracked (see https://github.com/dotnet/EntityFramework.Docs/issues/2205#issuecomment-1542914439). -#pragma warning disable CS0618 +#pragma warning disable CS0618 // Type or member is obsolete return _resourceDefinitionAccessor.IsReadOnlyRequest ? QueryTrackingBehavior.NoTrackingWithIdentityResolution : null; -#pragma warning restore CS0618 +#pragma warning restore CS0618 // Type or member is obsolete } /// diff --git a/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs b/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs index d4e3c156cb..678c308ed4 100644 --- a/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs +++ b/src/JsonApiDotNetCore/Resources/IdentifiableExtensions.cs @@ -4,7 +4,7 @@ namespace JsonApiDotNetCore.Resources; internal static class IdentifiableExtensions { - private const string IdPropertyName = nameof(Identifiable.Id); + private const string IdPropertyName = nameof(Identifiable<>.Id); public static object GetTypedId(this IIdentifiable identifiable) { diff --git a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs index 529c76a1d2..e66550fbac 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonConverters/ResourceObjectConverter.cs @@ -204,7 +204,7 @@ public override ResourceObject Read(ref Utf8JsonReader reader, Type typeToConver { object? attributeValue; - if (property.Name == nameof(Identifiable.Id)) + if (property.Name == nameof(Identifiable<>.Id)) { attributeValue = JsonInvalidAttributeInfo.Id; } diff --git a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs index df712d6eec..1fec12996f 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/LinkBuilder.cs @@ -22,11 +22,9 @@ public class LinkBuilder : ILinkBuilder private const string PageSizeParameterName = "page[size]"; private const string PageNumberParameterName = "page[number]"; - private static readonly string GetPrimaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController, int>.GetAsync)); - private static readonly string GetSecondaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController, int>.GetSecondaryAsync)); - - private static readonly string GetRelationshipControllerActionName = - NoAsyncSuffix(nameof(BaseJsonApiController, int>.GetRelationshipAsync)); + private static readonly string GetPrimaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<,>.GetAsync)); + private static readonly string GetSecondaryControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<,>.GetSecondaryAsync)); + private static readonly string GetRelationshipControllerActionName = NoAsyncSuffix(nameof(BaseJsonApiController<,>.GetRelationshipAsync)); private static readonly UriNormalizer UriNormalizer = new(); diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs index 80f7809255..036373b8a5 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResponseModelAdapter.cs @@ -247,7 +247,7 @@ protected virtual ResourceObject ConvertResource(IIdentifiable resource, Resourc foreach (AttrAttribute attr in resourceType.Attributes) { - if (!fieldSet.Contains(attr) || attr.Property.Name == nameof(Identifiable.Id)) + if (!fieldSet.Contains(attr) || attr.Property.Name == nameof(Identifiable<>.Id)) { continue; } diff --git a/test/AnnotationTests/AnnotationTests.csproj b/test/AnnotationTests/AnnotationTests.csproj index 885e9f769c..6d2bfff1e6 100644 --- a/test/AnnotationTests/AnnotationTests.csproj +++ b/test/AnnotationTests/AnnotationTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0;netstandard2.0 + net10.0;net9.0;net8.0;netstandard2.0 diff --git a/test/DapperTests/DapperTests.csproj b/test/DapperTests/DapperTests.csproj index 1d7fc6184d..d4b9a3e661 100644 --- a/test/DapperTests/DapperTests.csproj +++ b/test/DapperTests/DapperTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/DiscoveryTests/DiscoveryTests.csproj b/test/DiscoveryTests/DiscoveryTests.csproj index 11567b9113..d92d81790f 100644 --- a/test/DiscoveryTests/DiscoveryTests.csproj +++ b/test/DiscoveryTests/DiscoveryTests.csproj @@ -1,6 +1,6 @@  - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarExpressionRewriter.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarExpressionRewriter.cs index e7802bcb09..1e4234c259 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarExpressionRewriter.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/CompositeKeys/CarExpressionRewriter.cs @@ -75,7 +75,7 @@ public CarExpressionRewriter(IResourceGraph resourceGraph) private static bool IsCarId(PropertyInfo property) { - return property.Name == nameof(Identifiable.Id) && property.DeclaringType == typeof(Car); + return property.Name == nameof(Identifiable<>.Id) && property.DeclaringType == typeof(Car); } private QueryExpression RewriteFilterOnCarStringIds(ResourceFieldChainExpression existingCarIdChain, IEnumerable carStringIds) diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs index 78ab514636..3e66be191c 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/EagerLoading/Building.cs @@ -9,8 +9,6 @@ namespace JsonApiDotNetCoreTests.IntegrationTests.EagerLoading; [Resource(ControllerNamespace = "JsonApiDotNetCoreTests.IntegrationTests.EagerLoading")] public sealed class Building : Identifiable { - private string? _tempPrimaryDoorColor; - [Attr] public string Number { get; set; } = null!; @@ -24,7 +22,7 @@ public string PrimaryDoorColor { get { - if (_tempPrimaryDoorColor == null && PrimaryDoor == null) + if (field == null && PrimaryDoor == null) { // The ASP.NET model validator reads the value of this required property, to ensure it is not null. // When creating a resource, BuildingDefinition ensures a value is assigned. But when updating a resource @@ -33,7 +31,7 @@ public string PrimaryDoorColor return null!; } - return _tempPrimaryDoorColor ?? PrimaryDoor!.Color; + return field ?? PrimaryDoor!.Color; } set { @@ -41,7 +39,7 @@ public string PrimaryDoorColor { // A request body is being deserialized. At this time, related entities have not been loaded yet. // We cache the assigned value in a private field, so it can be used later. - _tempPrimaryDoorColor = value; + field = value; } else { diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs index 004fcb2121..cd420d0bd9 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceTests.cs @@ -112,7 +112,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => workItemInDatabase.DueAt.Should().Be(newWorkItem.DueAt); }); - PropertyInfo? property = typeof(WorkItem).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(WorkItem).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -160,7 +160,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => userAccountInDatabase.LastName.Should().Be(newUserAccount.LastName); }); - PropertyInfo? property = typeof(UserAccount).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(UserAccount).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -205,7 +205,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => groupInDatabase.Name.Should().Be(newGroup.Name); }); - PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs index 681dc034c2..160cd3864a 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Creating/CreateResourceWithClientGeneratedIdTests.cs @@ -78,7 +78,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => groupInDatabase.Name.Should().Be(groupName); }); - PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -132,7 +132,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => groupInDatabase.Name.Should().Be(groupName); }); - PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -183,7 +183,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => colorInDatabase.DisplayName.Should().Be(newColor.DisplayName); }); - PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -234,7 +234,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => colorInDatabase.DisplayName.Should().Be(newColor.DisplayName); }); - PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -290,7 +290,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => colorInDatabase.DisplayName.Should().Be(defaultName); }); - PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -473,7 +473,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => colorInDatabase.DisplayName.Should().Be(newColor.DisplayName); }); - PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } diff --git a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs index 428c4a30d3..cf8ec690b7 100644 --- a/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs +++ b/test/JsonApiDotNetCoreTests/IntegrationTests/ReadWrite/Updating/Resources/UpdateResourceTests.cs @@ -325,7 +325,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => groupInDatabase.IsPublic.Should().Be(existingGroup.IsPublic); }); - PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(WorkItemGroup).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } @@ -373,7 +373,7 @@ await _testContext.RunOnDatabaseAsync(async dbContext => colorInDatabase.DisplayName.Should().Be(newDisplayName); }); - PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable.Id)); + PropertyInfo? property = typeof(RgbColor).GetProperty(nameof(Identifiable<>.Id)); property.Should().NotBeNull(); property.PropertyType.Should().Be(); } diff --git a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj index bdfea682d9..e74bbc8d3d 100644 --- a/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj +++ b/test/JsonApiDotNetCoreTests/JsonApiDotNetCoreTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/JsonApiDotNetCoreTests/UnitTests/ResourceGraph/ResourceGraphBuilderTests.cs b/test/JsonApiDotNetCoreTests/UnitTests/ResourceGraph/ResourceGraphBuilderTests.cs index 6ed5b6c67a..597a5d9d89 100644 --- a/test/JsonApiDotNetCoreTests/UnitTests/ResourceGraph/ResourceGraphBuilderTests.cs +++ b/test/JsonApiDotNetCoreTests/UnitTests/ResourceGraph/ResourceGraphBuilderTests.cs @@ -413,7 +413,7 @@ public void Can_override_capabilities_on_Id_property() IResourceGraph resourceGraph = builder.Build(); ResourceType resourceType = resourceGraph.GetResourceType(); - AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable.Id)); + AttrAttribute idAttribute = resourceType.GetAttributeByPropertyName(nameof(Identifiable<>.Id)); idAttribute.Capabilities.Should().Be(AttrCapabilities.AllowFilter); } diff --git a/test/MultiDbContextTests/MultiDbContextTests.csproj b/test/MultiDbContextTests/MultiDbContextTests.csproj index e80f03c69e..524270f7e2 100644 --- a/test/MultiDbContextTests/MultiDbContextTests.csproj +++ b/test/MultiDbContextTests/MultiDbContextTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj index 4deb6b21cd..9ff82322ba 100644 --- a/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj +++ b/test/NoEntityFrameworkTests/NoEntityFrameworkTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/OpenApiKiotaEndToEndTests/AtomicOperations/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/AtomicOperations/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..e4edc93a03 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/AtomicOperations/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "E396A8DEC9B8AA50A277166B500AF7164B4B296C4E7AF2ED163083395177317DB961290BC614658E91819AD441C99647E9A5935E1E93E661317E83839646B2A5", + "descriptionLocation": "../../../OpenApiTests/AtomicOperations/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "AtomicOperationsClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.AtomicOperations.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/AttributeTypes/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/AttributeTypes/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..def1736d7e --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/AttributeTypes/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "470127ED08004882F740911D40E752D8ED53AB130D682C7FD37C01873438236B77BE5DEAE52799294A4633FBFE59880647FA24AADE21FDF11066C1A9CAEE8E9B", + "descriptionLocation": "../../../OpenApiTests/AttributeTypes/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "AttributeTypesClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.AttributeTypes.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ClientIdGenerationModes/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ClientIdGenerationModes/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..fc28efb7bf --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ClientIdGenerationModes/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "E3404B0AE786DB66FBB2136BB27DDFB3A30C109C791238950B8D7EF7BF58088743C3247DAEB6DA6468DF43EB316422D7B61B04AC5AB7E2710D5E0ADB563CA973", + "descriptionLocation": "../../../OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "ClientIdGenerationModesClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ClientIdGenerationModes.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/Headers/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/Headers/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..3ef4b9e4e1 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/Headers/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "1A99CE35AAC1973F25A917E2776BAE0CA542956018D97228B68F200D5156C09C161051FCC1EE0DD4A33CAAAA30338D743CEC978D8622FA2C3AB6BDAB00826DA7", + "descriptionLocation": "../../../OpenApiTests/Headers/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "HeadersClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.Headers.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/IdObfuscation/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/IdObfuscation/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..63b88fc628 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/IdObfuscation/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "31F174FA03099E656F9110485E8476F5E87714B5BB8897F75164B22360E7742473322929D63DDBB5B1ADBF68C2B13908B98F3D93E27C926B3F4F9C646E6F8366", + "descriptionLocation": "../../../OpenApiTests/IdObfuscation/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "IdObfuscationClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.IdObfuscation.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/Links/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/Links/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..ee2d122ea3 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/Links/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "162E0C524667DC8B14371D9D28DAA91D9980A2653617C73008E8FB7F0BCF2BF80962FE938F46F09A441CCBA431EDA2BF96300E661A86162C20856522E3FC24F0", + "descriptionLocation": "../../../OpenApiTests/Links/Enabled/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "LinksClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.Links.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/MixedControllers/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/MixedControllers/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..c9c8782a7d --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/MixedControllers/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "0766FE86CC36BDA58ED569AC64F9227C962D8A4A7D11DF94E2C6C4120159610528F063AFB5BDAE8EC8B5D0D7B1C87A7EDF3EF9EA440B795CDE236F37694C0776", + "descriptionLocation": "../../../OpenApiTests/MixedControllers/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "MixedControllersClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.MixedControllers.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..b9820010aa --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ModelStateValidation/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "D23DFE7A559ED17024B4C3751668DB944EECB2B12184B515958D5AB9EBB7BC2632277A3C1D61CFB09764A9AC7A9CD58F006EBD60F79451E5D3748E503537A8D7", + "descriptionLocation": "../../../OpenApiTests/ModelStateValidation/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "ModelStateValidationClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ModelStateValidation.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj b/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj index 4e5e8dded9..b3c6215934 100644 --- a/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj +++ b/test/OpenApiKiotaEndToEndTests/OpenApiKiotaEndToEndTests.csproj @@ -1,6 +1,6 @@ - net9.0 + net10.0 diff --git a/test/OpenApiKiotaEndToEndTests/QueryStrings/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/QueryStrings/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..5a68388df6 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/QueryStrings/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "4C9E3EEF14CF44CD2E23C28EE2E4D0FF9D49F130DF4572FB1393E43344DC817EBEF92999A8093FD7BF21242108E0EA37BD38E86808DD9AB256A3F0467C6D80D4", + "descriptionLocation": "../../../OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "QueryStringsClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.QueryStrings.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ResourceInheritance/NoOperations/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/NoOperations/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..b21354886f --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/NoOperations/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "D17B596209906FEDB8DEEB009C0D5AD867F633F0B120EB297A2F8149E6D9CEC89FCB9175817EC77C1163E98EF1DDB89548EF4CCC4C4C26B925FF67FEF2FBB0E4", + "descriptionLocation": "../../../../OpenApiTests/ResourceInheritance/NoOperations/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "NoOperationsInheritanceClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ResourceInheritance.NoOperations.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ResourceInheritance/OnlyRelationships/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/OnlyRelationships/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..160fb8434e --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/OnlyRelationships/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "4DB0EB44353753EA3219D40CD202FCA401EAD5E4A760A40B9FAD50EF913F3D5306B2A1228C92BE886F7DFE3F95F77627272A67D8A1E4BDB02F909034012C26E6", + "descriptionLocation": "../../../../OpenApiTests/ResourceInheritance/OnlyRelationships/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "OnlyRelationshipsInheritanceClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ResourceInheritance.OnlyRelationships.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfOperations/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfOperations/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..d371893754 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfOperations/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "027F7817A47351EF48A63D99EA54A9168DFBF1365CE9F46C49481F2DBDB3EA6AEE32BAAE3E9D6EC6DC9D40A1E7B79ADCF11E43E426A36CCC1E64B00137B8F018", + "descriptionLocation": "../../../../OpenApiTests/ResourceInheritance/SubsetOfOperations/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "SubsetOfOperationsInheritanceClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ResourceInheritance.SubsetOfOperations.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfVarious/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfVarious/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..4b3b427b6b --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/ResourceInheritance/SubsetOfVarious/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "000C75DD85639C4386B001907E0BB38E4C6E8FB184F8B4CDA1D4D174C03778881CA74F6ED1315BFF409B5855A14AE25D706E2E4A23C95238BABACFF2AA7AB1BC", + "descriptionLocation": "../../../../OpenApiTests/ResourceInheritance/SubsetOfVarious/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "SubsetOfVariousInheritanceClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.ResourceInheritance.SubsetOfVarious.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiKiotaEndToEndTests/RestrictedControllers/GeneratedCode/kiota-lock.json b/test/OpenApiKiotaEndToEndTests/RestrictedControllers/GeneratedCode/kiota-lock.json new file mode 100644 index 0000000000..1019346939 --- /dev/null +++ b/test/OpenApiKiotaEndToEndTests/RestrictedControllers/GeneratedCode/kiota-lock.json @@ -0,0 +1,37 @@ +{ + "descriptionHash": "BA45DAE2194A0C5FF2DD6A602A8513729ADAD7DF5F8B9C7C112A90C1162556B76FF931A1661D8D76470265A371B68E453B584D9DAACA39D90DB6621CE5AE6B39", + "descriptionLocation": "../../../OpenApiTests/RestrictedControllers/GeneratedSwagger/swagger.g.json", + "lockFileVersion": "1.0.0", + "kiotaVersion": "1.29.0", + "clientClassName": "RestrictedControllersClient", + "typeAccessModifier": "Public", + "clientNamespaceName": "OpenApiKiotaEndToEndTests.RestrictedControllers.GeneratedCode", + "language": "CSharp", + "usesBackingStore": true, + "excludeBackwardCompatible": true, + "includeAdditionalData": true, + "disableSSLValidation": false, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Multipart.MultipartSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain;q=0.9", + "application/x-www-form-urlencoded;q=0.2", + "multipart/form-data;q=0.1" + ], + "includePatterns": [], + "excludePatterns": [], + "disabledValidationRules": [ + "KnownAndNotSupportedFormats", + "InconsistentTypeFormatPair" + ] +} \ No newline at end of file diff --git a/test/OpenApiNSwagClientTests/.editorconfig b/test/OpenApiNSwagClientTests/.editorconfig deleted file mode 100644 index e2ec1cac44..0000000000 --- a/test/OpenApiNSwagClientTests/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -# Workaround for incorrect nullability in NSwag generated clients. -[*Client.cs] -dotnet_diagnostic.CS8765.severity = none diff --git a/test/OpenApiNSwagClientTests/ChangeTracking/SerializerChangeTrackingTests.cs b/test/OpenApiNSwagClientTests/ChangeTracking/SerializerChangeTrackingTests.cs index fb1e1f4b19..b0ca3c51bc 100644 --- a/test/OpenApiNSwagClientTests/ChangeTracking/SerializerChangeTrackingTests.cs +++ b/test/OpenApiNSwagClientTests/ChangeTracking/SerializerChangeTrackingTests.cs @@ -802,13 +802,11 @@ public async Task Can_track_multiple_times_in_same_request_document() [UsedImplicitly(ImplicitUseTargetFlags.Members)] private sealed class ComplexType : NotifyPropertySet { - private DateTime? _nullableDateTime; - [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] public DateTime? NullableDateTime { - get => _nullableDateTime; - set => SetProperty(ref _nullableDateTime, value); + get; + set => SetProperty(ref field, value); } public NestedType? NestedType { get; set; } @@ -817,21 +815,18 @@ public DateTime? NullableDateTime [UsedImplicitly(ImplicitUseTargetFlags.Members)] private sealed class NestedType : NotifyPropertySet { - private int? _nullableInt; - private string? _nullableString; - [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] public int? NullableInt { - get => _nullableInt; - set => SetProperty(ref _nullableInt, value); + get; + set => SetProperty(ref field, value); } [JsonProperty(NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)] public string? NullableString { - get => _nullableString; - set => SetProperty(ref _nullableString, value); + get; + set => SetProperty(ref field, value); } } diff --git a/test/OpenApiNSwagClientTests/OpenApiNSwagClientTests.csproj b/test/OpenApiNSwagClientTests/OpenApiNSwagClientTests.csproj index b005d3786f..552d4e74c1 100644 --- a/test/OpenApiNSwagClientTests/OpenApiNSwagClientTests.csproj +++ b/test/OpenApiNSwagClientTests/OpenApiNSwagClientTests.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 @@ -20,7 +20,6 @@ - diff --git a/test/OpenApiNSwagEndToEndTests/.editorconfig b/test/OpenApiNSwagEndToEndTests/.editorconfig deleted file mode 100644 index e2ec1cac44..0000000000 --- a/test/OpenApiNSwagEndToEndTests/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -# Workaround for incorrect nullability in NSwag generated clients. -[*Client.cs] -dotnet_diagnostic.CS8765.severity = none diff --git a/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj b/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj index 0c716f4be5..c05f841f49 100644 --- a/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj +++ b/test/OpenApiNSwagEndToEndTests/OpenApiNSwagEndToEndTests.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 @@ -20,7 +20,6 @@ - diff --git a/test/OpenApiTests/AtomicOperations/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/AtomicOperations/GeneratedSwagger/swagger.g.json index 613c50890c..b98e7ec750 100644 --- a/test/OpenApiTests/AtomicOperations/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/AtomicOperations/GeneratedSwagger/swagger.g.json @@ -3678,5 +3678,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "operations" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/AttributeTypes/AttributeTypesStartup.cs b/test/OpenApiTests/AttributeTypes/AttributeTypesStartup.cs index c71515104e..dcb6fda17c 100644 --- a/test/OpenApiTests/AttributeTypes/AttributeTypesStartup.cs +++ b/test/OpenApiTests/AttributeTypes/AttributeTypesStartup.cs @@ -6,7 +6,7 @@ using JsonApiDotNetCore.Configuration; using JsonApiDotNetCore.Queries.Parsing; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace OpenApiTests.AttributeTypes; @@ -41,52 +41,52 @@ protected override void ConfigureSwaggerGenOptions(SwaggerGenOptions options) options.MapType(() => new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }); options.MapType(() => new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }); options.MapType(() => new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }); options.MapType(() => new OpenApiSchema { - Type = "number", + Type = JsonSchemaType.Number, Format = "float" }); options.MapType(() => new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }); options.MapType(() => new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, MaxLength = 4 }); options.MapType(() => new OpenApiSchema { // Beware that "duration" does not round-trip universally. NSwag and Kiota are incompatible. - Type = "string", + Type = JsonSchemaType.String, Format = "duration" }); options.MapType(() => new OpenApiSchema { - Type = "string", + Type = JsonSchemaType.String, Format = "ipv4" }); options.MapType(() => new OpenApiSchema { - Type = "string" + Type = JsonSchemaType.String }); } diff --git a/test/OpenApiTests/AttributeTypes/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/AttributeTypes/GeneratedSwagger/swagger.g.json index 370668bcd6..09240ea079 100644 --- a/test/OpenApiTests/AttributeTypes/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/AttributeTypes/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1991,5 +1991,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "typeContainers" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json index 0a32442e86..b230d33a26 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ClientIdGenerationModes/GeneratedSwagger/swagger.g.json @@ -1629,5 +1629,16 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "games" + }, + { + "name": "playerGroups" + }, + { + "name": "players" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/Documentation/DocumentationStartup.cs b/test/OpenApiTests/Documentation/DocumentationStartup.cs index 2e6aaa78cd..f38796f936 100644 --- a/test/OpenApiTests/Documentation/DocumentationStartup.cs +++ b/test/OpenApiTests/Documentation/DocumentationStartup.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using TestBuildingBlocks; diff --git a/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json index 735ed22d80..c7eef9d8cf 100644 --- a/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/Headers/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2245,5 +2245,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "countries" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/IdObfuscation/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/IdObfuscation/GeneratedSwagger/swagger.g.json index d582e1d8f8..f4b3cc82c6 100644 --- a/test/OpenApiTests/IdObfuscation/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/IdObfuscation/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -401,13 +401,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -415,6 +408,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -709,13 +709,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -723,6 +716,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -883,13 +883,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -897,6 +890,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1263,13 +1263,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1277,6 +1270,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1546,13 +1546,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1560,6 +1553,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1854,13 +1854,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1868,6 +1861,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2028,13 +2028,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2042,6 +2035,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4316,5 +4316,16 @@ "type": "string" } } - } + }, + "tags": [ + { + "name": "bankAccounts" + }, + { + "name": "debitCards" + }, + { + "name": "operations" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/LegacyOpenApi/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/LegacyOpenApi/GeneratedSwagger/swagger.g.json index f72814347b..1891fd84de 100644 --- a/test/OpenApiTests/LegacyOpenApi/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/LegacyOpenApi/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -401,13 +401,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -415,6 +408,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -709,13 +709,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -723,6 +716,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -883,13 +883,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -897,6 +890,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1263,13 +1263,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1277,6 +1270,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1546,13 +1546,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1560,6 +1553,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1854,13 +1854,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1868,6 +1861,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2028,13 +2028,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2042,6 +2035,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2436,13 +2436,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2450,6 +2443,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2610,13 +2610,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2624,6 +2617,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2990,13 +2990,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3004,6 +2997,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3273,13 +3273,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3287,6 +3280,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3581,13 +3581,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3595,6 +3588,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3755,13 +3755,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3769,6 +3762,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4007,13 +4007,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4021,6 +4014,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4181,13 +4181,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4195,6 +4188,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4589,13 +4589,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4603,6 +4596,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4763,13 +4763,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4777,6 +4770,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5171,13 +5171,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5185,6 +5178,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5345,13 +5345,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5359,6 +5352,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5569,13 +5569,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5583,6 +5576,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5852,13 +5852,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5866,6 +5859,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8854,5 +8854,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "airplanes" + }, + { + "name": "flight-attendants" + }, + { + "name": "flights" + }, + { + "name": "passengers" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/LegacyOpenApi/expected-swagger.json b/test/OpenApiTests/LegacyOpenApi/expected-swagger.json index f72814347b..1891fd84de 100644 --- a/test/OpenApiTests/LegacyOpenApi/expected-swagger.json +++ b/test/OpenApiTests/LegacyOpenApi/expected-swagger.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -401,13 +401,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -415,6 +408,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -709,13 +709,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -723,6 +716,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -883,13 +883,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -897,6 +890,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1263,13 +1263,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1277,6 +1270,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1546,13 +1546,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1560,6 +1553,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1854,13 +1854,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1868,6 +1861,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2028,13 +2028,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2042,6 +2035,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2436,13 +2436,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2450,6 +2443,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2610,13 +2610,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2624,6 +2617,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2990,13 +2990,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3004,6 +2997,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3273,13 +3273,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3287,6 +3280,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3581,13 +3581,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3595,6 +3588,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3755,13 +3755,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3769,6 +3762,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4007,13 +4007,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4021,6 +4014,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4181,13 +4181,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4195,6 +4188,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4589,13 +4589,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4603,6 +4596,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4763,13 +4763,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4777,6 +4770,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5171,13 +5171,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5185,6 +5178,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5345,13 +5345,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5359,6 +5352,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5569,13 +5569,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5583,6 +5576,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5852,13 +5852,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5866,6 +5859,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8854,5 +8854,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "airplanes" + }, + { + "name": "flight-attendants" + }, + { + "name": "flights" + }, + { + "name": "passengers" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/Links/Enabled/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/Links/Enabled/GeneratedSwagger/swagger.g.json index b72c15aecb..2814116d8a 100644 --- a/test/OpenApiTests/Links/Enabled/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/Links/Enabled/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -689,13 +689,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -703,6 +696,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -976,13 +976,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -990,6 +983,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1260,13 +1260,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1274,6 +1267,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1547,13 +1547,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1561,6 +1554,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1831,13 +1831,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1845,6 +1838,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2118,13 +2118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2132,6 +2125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2434,13 +2434,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2448,6 +2441,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2612,13 +2612,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2626,6 +2619,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2870,13 +2870,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2884,6 +2877,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3048,13 +3048,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3062,6 +3055,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3466,13 +3466,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3480,6 +3473,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3644,13 +3644,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3658,6 +3651,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6009,5 +6009,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "accommodations" + }, + { + "name": "excursions" + }, + { + "name": "transports" + }, + { + "name": "vacations" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/MixedControllers/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/MixedControllers/GeneratedSwagger/swagger.g.json index a7a9229cd5..e5bf0b32a7 100644 --- a/test/OpenApiTests/MixedControllers/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/MixedControllers/GeneratedSwagger/swagger.g.json @@ -352,13 +352,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -366,6 +359,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1358,5 +1358,19 @@ "type": "string" } } - } + }, + "tags": [ + { + "name": "coffeeSummaries" + }, + { + "name": "cupOfCoffees" + }, + { + "name": "emails" + }, + { + "name": "fileTransfers" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/swagger.g.json index 05a83c87ee..b5b955524a 100644 --- a/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ModelStateValidation/GeneratedSwagger/swagger.g.json @@ -1060,5 +1060,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "socialMediaAccounts" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json index 8454946d0c..396c94ccee 100644 --- a/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/CamelCase/GeneratedSwagger/swagger.g.json @@ -207,13 +207,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -221,6 +214,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -494,13 +494,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -508,6 +501,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -778,13 +778,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -792,6 +785,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1065,13 +1065,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1079,6 +1072,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1381,13 +1381,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1395,6 +1388,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1559,13 +1559,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1573,6 +1566,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1817,13 +1817,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1831,6 +1824,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1995,13 +1995,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2009,6 +2002,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2413,13 +2413,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2427,6 +2420,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2591,13 +2591,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2605,6 +2598,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5075,5 +5075,16 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "operations" + }, + { + "name": "staffMembers" + }, + { + "name": "supermarkets" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json index c48444fbea..865e3ba235 100644 --- a/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/KebabCase/GeneratedSwagger/swagger.g.json @@ -207,13 +207,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -221,6 +214,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -494,13 +494,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -508,6 +501,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -778,13 +778,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -792,6 +785,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1065,13 +1065,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1079,6 +1072,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1381,13 +1381,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1395,6 +1388,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1559,13 +1559,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1573,6 +1566,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1817,13 +1817,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1831,6 +1824,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1995,13 +1995,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2009,6 +2002,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2413,13 +2413,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2427,6 +2420,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2591,13 +2591,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2605,6 +2598,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5075,5 +5075,16 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "operations" + }, + { + "name": "staff-members" + }, + { + "name": "supermarkets" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json index bb924ef157..8fd99be448 100644 --- a/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/NamingConventions/PascalCase/GeneratedSwagger/swagger.g.json @@ -207,13 +207,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -221,6 +214,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -494,13 +494,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -508,6 +501,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -778,13 +778,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -792,6 +785,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1065,13 +1065,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1079,6 +1072,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1381,13 +1381,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1395,6 +1388,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1559,13 +1559,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1573,6 +1566,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1817,13 +1817,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1831,6 +1824,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1995,13 +1995,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2009,6 +2002,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2413,13 +2413,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2427,6 +2420,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2591,13 +2591,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2605,6 +2598,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5075,5 +5075,16 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "operations" + }, + { + "name": "StaffMembers" + }, + { + "name": "Supermarkets" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj index 8d9336f54a..5700b77798 100644 --- a/test/OpenApiTests/OpenApiTests.csproj +++ b/test/OpenApiTests/OpenApiTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 True false $(NoWarn);1591 diff --git a/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json index 4b5473246c..8c619392b3 100644 --- a/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/QueryStrings/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1125,13 +1125,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1139,6 +1132,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1412,13 +1412,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1426,6 +1419,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1728,13 +1728,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1742,6 +1735,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1906,13 +1906,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1920,6 +1913,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2324,13 +2324,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2338,6 +2331,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2502,13 +2502,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2516,6 +2509,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2760,13 +2760,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2774,6 +2767,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2938,13 +2938,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2952,6 +2945,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4993,5 +4993,13 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "nameValuePairs" + }, + { + "name": "nodes" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json index 541a5901ba..f3bba9df82 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1317,13 +1317,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1331,6 +1324,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1495,13 +1495,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1509,6 +1502,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1753,13 +1753,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1767,6 +1760,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1931,13 +1931,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1945,6 +1938,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2349,13 +2349,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2363,6 +2356,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2527,13 +2527,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2541,6 +2534,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3951,5 +3951,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "resources" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json index cb83b19b17..30b371eb06 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1317,13 +1317,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1331,6 +1324,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1495,13 +1495,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1509,6 +1502,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1753,13 +1753,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1767,6 +1760,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1931,13 +1931,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1945,6 +1938,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2349,13 +2349,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2363,6 +2356,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2527,13 +2527,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2541,6 +2534,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4061,5 +4061,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "resources" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json index 1b962dc499..37db4b7d2f 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1157,13 +1157,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1171,6 +1164,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1335,13 +1335,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1349,6 +1342,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1593,13 +1593,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1607,6 +1600,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1771,13 +1771,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1785,6 +1778,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2029,13 +2029,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2043,6 +2036,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2207,13 +2207,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2221,6 +2214,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2465,13 +2465,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2479,6 +2472,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2643,13 +2643,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2657,6 +2650,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3061,13 +3061,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3075,6 +3068,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3239,13 +3239,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3253,6 +3246,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5006,5 +5006,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "resources" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json index 0717249aa5..1a88111b71 100644 --- a/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1157,13 +1157,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1171,6 +1164,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1335,13 +1335,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1349,6 +1342,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1593,13 +1593,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1607,6 +1600,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1771,13 +1771,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1785,6 +1778,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2029,13 +2029,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2043,6 +2036,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2207,13 +2207,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2221,6 +2214,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2465,13 +2465,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2479,6 +2472,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2643,13 +2643,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2657,6 +2650,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3061,13 +3061,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3075,6 +3068,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3239,13 +3239,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3253,6 +3246,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5000,5 +5000,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "resources" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceInheritance/NoOperations/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceInheritance/NoOperations/GeneratedSwagger/swagger.g.json index 9dad8a07cb..18fa97e43b 100644 --- a/test/OpenApiTests/ResourceInheritance/NoOperations/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceInheritance/NoOperations/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -405,13 +405,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -419,6 +412,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -721,13 +721,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -735,6 +728,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -899,13 +899,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -913,6 +906,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1125,13 +1125,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1139,6 +1132,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1412,13 +1412,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1426,6 +1419,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1728,13 +1728,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1742,6 +1735,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1906,13 +1906,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1920,6 +1913,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2132,13 +2132,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2146,6 +2139,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2419,13 +2419,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2433,6 +2426,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2703,13 +2703,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2717,6 +2710,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2990,13 +2990,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3004,6 +2997,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3274,13 +3274,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3288,6 +3281,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3561,13 +3561,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3575,6 +3568,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3877,13 +3877,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3891,6 +3884,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4055,13 +4055,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4069,6 +4062,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4473,13 +4473,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4487,6 +4480,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4651,13 +4651,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4665,6 +4658,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5037,13 +5037,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5051,6 +5044,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5324,13 +5324,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5338,6 +5331,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5640,13 +5640,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5654,6 +5647,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5818,13 +5818,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -5832,6 +5825,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6204,13 +6204,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -6218,6 +6211,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6491,13 +6491,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -6505,6 +6498,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6807,13 +6807,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -6821,6 +6814,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6985,13 +6985,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -6999,6 +6992,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -7211,13 +7211,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -7225,6 +7218,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -7498,13 +7498,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -7512,6 +7505,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -7814,13 +7814,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -7828,6 +7821,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -7992,13 +7992,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -8006,6 +7999,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8218,13 +8218,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -8232,6 +8225,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8505,13 +8505,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -8519,6 +8512,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8821,13 +8821,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -8835,6 +8828,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -8999,13 +8999,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -9013,6 +9006,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -9417,13 +9417,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -9431,6 +9424,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -9595,13 +9595,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -9609,6 +9602,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -10070,13 +10070,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -10084,6 +10077,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -10357,13 +10357,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -10371,6 +10364,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -10673,13 +10673,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -10687,6 +10680,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -10851,13 +10851,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -10865,6 +10858,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -11237,13 +11237,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -11251,6 +11244,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -11524,13 +11524,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -11538,6 +11531,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -11808,13 +11808,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -11822,6 +11815,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -12095,13 +12095,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -12109,6 +12102,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -12411,13 +12411,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -12425,6 +12418,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -12589,13 +12589,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -12603,6 +12596,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -12815,13 +12815,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -12829,6 +12822,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -13102,13 +13102,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -13116,6 +13109,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -13386,13 +13386,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -13400,6 +13393,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -13673,13 +13673,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -13687,6 +13680,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -13989,13 +13989,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -14003,6 +13996,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -14167,13 +14167,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -14181,6 +14174,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -20009,5 +20009,52 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "bathrooms" + }, + { + "name": "bedrooms" + }, + { + "name": "buildings" + }, + { + "name": "cyclePaths" + }, + { + "name": "districts" + }, + { + "name": "familyHomes" + }, + { + "name": "kitchens" + }, + { + "name": "livingRooms" + }, + { + "name": "mansions" + }, + { + "name": "operations" + }, + { + "name": "residences" + }, + { + "name": "roads" + }, + { + "name": "rooms" + }, + { + "name": "staffMembers" + }, + { + "name": "toilets" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceInheritance/OnlyRelationships/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceInheritance/OnlyRelationships/GeneratedSwagger/swagger.g.json index 4f80315108..508e052ce8 100644 --- a/test/OpenApiTests/ResourceInheritance/OnlyRelationships/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceInheritance/OnlyRelationships/GeneratedSwagger/swagger.g.json @@ -150,13 +150,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -164,6 +157,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -408,13 +408,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -422,6 +415,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -666,13 +666,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -680,6 +673,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1084,13 +1084,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1098,6 +1091,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1502,13 +1502,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1516,6 +1509,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1920,13 +1920,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1934,6 +1927,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2178,13 +2178,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2192,6 +2185,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2436,13 +2436,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2450,6 +2443,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2854,13 +2854,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2868,6 +2861,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3361,13 +3361,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3375,6 +3368,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3779,13 +3779,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3793,6 +3786,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -4037,13 +4037,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -4051,6 +4044,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -6190,5 +6190,40 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "bathrooms" + }, + { + "name": "bedrooms" + }, + { + "name": "districts" + }, + { + "name": "familyHomes" + }, + { + "name": "kitchens" + }, + { + "name": "livingRooms" + }, + { + "name": "mansions" + }, + { + "name": "operations" + }, + { + "name": "residences" + }, + { + "name": "rooms" + }, + { + "name": "toilets" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceInheritance/SubsetOfOperations/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceInheritance/SubsetOfOperations/GeneratedSwagger/swagger.g.json index 48e39eb3ac..e1b1b75353 100644 --- a/test/OpenApiTests/ResourceInheritance/SubsetOfOperations/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceInheritance/SubsetOfOperations/GeneratedSwagger/swagger.g.json @@ -3254,5 +3254,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "operations" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/ResourceInheritance/SubsetOfVarious/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/ResourceInheritance/SubsetOfVarious/GeneratedSwagger/swagger.g.json index 42301a5d4d..ed0c8faa55 100644 --- a/test/OpenApiTests/ResourceInheritance/SubsetOfVarious/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/ResourceInheritance/SubsetOfVarious/GeneratedSwagger/swagger.g.json @@ -369,13 +369,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -383,6 +376,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -515,13 +515,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -529,6 +522,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -690,13 +690,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -704,6 +697,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3365,5 +3365,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "buildings" + }, + { + "name": "cyclePaths" + }, + { + "name": "districts" + }, + { + "name": "familyHomes" + } + ] } \ No newline at end of file diff --git a/test/OpenApiTests/RestrictedControllers/GeneratedSwagger/swagger.g.json b/test/OpenApiTests/RestrictedControllers/GeneratedSwagger/swagger.g.json index 51cc607974..70f5bf303e 100644 --- a/test/OpenApiTests/RestrictedControllers/GeneratedSwagger/swagger.g.json +++ b/test/OpenApiTests/RestrictedControllers/GeneratedSwagger/swagger.g.json @@ -118,13 +118,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -132,6 +125,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -293,13 +293,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -307,6 +300,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -439,13 +439,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -453,6 +446,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -614,13 +614,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -628,6 +621,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -792,13 +792,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -806,6 +799,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -970,13 +970,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -984,6 +977,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1148,13 +1148,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1162,6 +1155,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1326,13 +1326,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1340,6 +1333,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1504,13 +1504,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1518,6 +1511,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1682,13 +1682,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1696,6 +1689,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -1828,13 +1828,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -1842,6 +1835,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2003,13 +2003,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2017,6 +2010,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2181,13 +2181,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2195,6 +2188,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2359,13 +2359,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2373,6 +2366,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2537,13 +2537,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2551,6 +2544,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -2715,13 +2715,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -2729,6 +2722,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3133,13 +3133,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3147,6 +3140,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -3391,13 +3391,6 @@ "200": { "description": "The operation completed successfully.", "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, "Content-Length": { "description": "Size of the HTTP response body, in bytes.", "required": true, @@ -3405,6 +3398,13 @@ "type": "integer", "format": "int64" } + }, + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } } }, @@ -5910,5 +5910,22 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "dataStreams" + }, + { + "name": "readOnlyChannels" + }, + { + "name": "readOnlyResourceChannels" + }, + { + "name": "relationshipChannels" + }, + { + "name": "writeOnlyChannels" + } + ] } \ No newline at end of file diff --git a/test/SourceGeneratorTests/SourceGeneratorTests.csproj b/test/SourceGeneratorTests/SourceGeneratorTests.csproj index 4f487fa168..61946fbc1a 100644 --- a/test/SourceGeneratorTests/SourceGeneratorTests.csproj +++ b/test/SourceGeneratorTests/SourceGeneratorTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/TestBuildingBlocks/CapturingLoggerProvider.cs b/test/TestBuildingBlocks/CapturingLoggerProvider.cs index 07366f5db1..d0894696a1 100644 --- a/test/TestBuildingBlocks/CapturingLoggerProvider.cs +++ b/test/TestBuildingBlocks/CapturingLoggerProvider.cs @@ -1,5 +1,12 @@ using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using LockPrimitive = +#if NET9_0_OR_GREATER + System.Threading.Lock +#else + object +#endif + ; namespace TestBuildingBlocks; @@ -9,11 +16,7 @@ public sealed class CapturingLoggerProvider : ILoggerProvider private static readonly Func DefaultFilter = (_, _) => true; private readonly Func _filter; -#if NET8_0 - private readonly object _lockObject = new(); -#else - private readonly Lock _lockObject = new(); -#endif + private readonly LockPrimitive _lockObject = new(); private readonly List _messages = []; public CapturingLoggerProvider() diff --git a/test/TestBuildingBlocks/TestBuildingBlocks.csproj b/test/TestBuildingBlocks/TestBuildingBlocks.csproj index 4d66eac8a6..3174e64374 100644 --- a/test/TestBuildingBlocks/TestBuildingBlocks.csproj +++ b/test/TestBuildingBlocks/TestBuildingBlocks.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0 diff --git a/test/TestBuildingBlocks/Unknown.cs b/test/TestBuildingBlocks/Unknown.cs index a777435228..79227fddf3 100644 --- a/test/TestBuildingBlocks/Unknown.cs +++ b/test/TestBuildingBlocks/Unknown.cs @@ -79,7 +79,7 @@ private static string InnerFor(bool isAlt) } throw new NotSupportedException( - $"Unsupported '{nameof(Identifiable.Id)}' property of type '{type}' on resource type '{typeof(TResource).Name}'."); + $"Unsupported '{nameof(Identifiable<>.Id)}' property of type '{type}' on resource type '{typeof(TResource).Name}'."); } } } diff --git a/test/TestBuildingBlocks/XUnitLogHttpMessageHandler.cs b/test/TestBuildingBlocks/XUnitLogHttpMessageHandler.cs index 4b981f81d2..514ba19c51 100644 --- a/test/TestBuildingBlocks/XUnitLogHttpMessageHandler.cs +++ b/test/TestBuildingBlocks/XUnitLogHttpMessageHandler.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Logging; using Xunit.Abstractions; +#pragma warning disable CA1873 // Avoid potentially expensive logging + namespace TestBuildingBlocks; /// diff --git a/test/UnitTests/UnitTests.csproj b/test/UnitTests/UnitTests.csproj index 68076a51e1..503547d0c5 100644 --- a/test/UnitTests/UnitTests.csproj +++ b/test/UnitTests/UnitTests.csproj @@ -1,6 +1,6 @@ - net9.0;net8.0 + net10.0;net9.0;net8.0