diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 435201037d..4b173d25f2 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,28 +3,39 @@
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
- "version": "2024.1.4",
+ "version": "2024.3.6",
"commands": [
"jb"
- ]
+ ],
+ "rollForward": false
},
"regitlint": {
- "version": "6.3.12",
+ "version": "6.3.13",
"commands": [
"regitlint"
- ]
+ ],
+ "rollForward": false
},
"dotnet-reportgenerator-globaltool": {
- "version": "5.3.6",
+ "version": "5.4.5",
"commands": [
"reportgenerator"
- ]
+ ],
+ "rollForward": false
},
"docfx": {
- "version": "2.76.0",
+ "version": "2.78.2",
"commands": [
"docfx"
- ]
+ ],
+ "rollForward": false
+ },
+ "microsoft.openapi.kiota": {
+ "version": "1.25.1",
+ "commands": [
+ "kiota"
+ ],
+ "rollForward": false
}
}
}
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index 5a036d1797..2e5c1061b9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,60 +4,125 @@ root = true
[*]
indent_style = space
indent_size = 4
+tab-width = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
-[*.{config,csproj,css,js,json,props,ruleset,xslt,html}]
+[*.{config,csproj,css,js,json,props,targets,xml,ruleset,xsd,xslt,html,yml,yaml}]
indent_size = 2
+tab-width = 2
+max_line_length = 160
+
+[*.{cs,cshtml,ascx,aspx}]
-[*.{cs}]
#### C#/.NET Coding Conventions ####
+# Default severity for IDE* analyzers with category 'Style'
+# Note: specific rules below use severity silent, because Resharper code cleanup auto-fixes them.
+dotnet_analyzer_diagnostic.category-Style.severity = warning
+
# 'using' directive preferences
dotnet_sort_system_directives_first = true
-csharp_using_directive_placement = outside_namespace:suggestion
+csharp_using_directive_placement = outside_namespace:silent
+# IDE0005: Remove unnecessary import
+dotnet_diagnostic.IDE0005.severity = silent
# Namespace declarations
-csharp_style_namespace_declarations = file_scoped:suggestion
+csharp_style_namespace_declarations = file_scoped:silent
+# IDE0160: Use block-scoped namespace
+dotnet_diagnostic.IDE0160.severity = silent
+# IDE0161: Use file-scoped namespace
+dotnet_diagnostic.IDE0161.severity = silent
# this. preferences
-dotnet_style_qualification_for_field = false:suggestion
-dotnet_style_qualification_for_property = false:suggestion
-dotnet_style_qualification_for_method = false:suggestion
-dotnet_style_qualification_for_event = false:suggestion
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_property = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_event = false:silent
+# IDE0003: Remove this or Me qualification
+dotnet_diagnostic.IDE0003.severity = silent
+# IDE0009: Add this or Me qualification
+dotnet_diagnostic.IDE0009.severity = silent
# Language keywords vs BCL types preferences
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+# IDE0049: Use language keywords instead of framework type names for type references
+dotnet_diagnostic.IDE0049.severity = silent
# Modifier preferences
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
-csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion
-csharp_style_pattern_local_over_anonymous_function = false:silent
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+# IDE0040: Add accessibility modifiers
+dotnet_diagnostic.IDE0040.severity = silent
+csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:silent
+# IDE0036: Order modifiers
+dotnet_diagnostic.IDE0036.severity = silent
# Expression-level preferences
dotnet_style_operator_placement_when_wrapping = end_of_line
-dotnet_style_prefer_auto_properties = true:suggestion
-dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
-dotnet_style_prefer_conditional_expression_over_return = true:suggestion
-csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
+dotnet_style_prefer_auto_properties = true:silent
+# IDE0032: Use auto property
+dotnet_diagnostic.IDE0032.severity = silent
+dotnet_style_prefer_conditional_expression_over_assignment = true:silent
+# IDE0045: Use conditional expression for assignment
+dotnet_diagnostic.IDE0045.severity = silent
+dotnet_style_prefer_conditional_expression_over_return = true:silent
+# IDE0046: Use conditional expression for return
+dotnet_diagnostic.IDE0046.severity = silent
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+# IDE0058: Remove unused expression value
+dotnet_diagnostic.IDE0058.severity = silent
+
+# Collection expression preferences (note: partially turned off in Directory.Build.props)
+dotnet_style_prefer_collection_expression = when_types_exactly_match
# Parameter preferences
-dotnet_code_quality_unused_parameters = non_public:suggestion
+dotnet_code_quality_unused_parameters = non_public
+
+# Local functions vs lambdas
+csharp_style_prefer_local_over_anonymous_function = false:silent
+# IDE0039: Use local function instead of lambda
+dotnet_diagnostic.IDE0039.severity = silent
# Expression-bodied members
-csharp_style_expression_bodied_accessors = true:suggestion
-csharp_style_expression_bodied_constructors = false:suggestion
-csharp_style_expression_bodied_indexers = true:suggestion
-csharp_style_expression_bodied_lambdas = true:suggestion
-csharp_style_expression_bodied_local_functions = false:suggestion
-csharp_style_expression_bodied_methods = false:suggestion
-csharp_style_expression_bodied_operators = false:suggestion
-csharp_style_expression_bodied_properties = true:suggestion
+csharp_style_expression_bodied_accessors = true:silent
+# IDE0027: Use expression body for accessors
+dotnet_diagnostic.IDE0027.severity = silent
+csharp_style_expression_bodied_constructors = false:silent
+# IDE0021: Use expression body for constructors
+dotnet_diagnostic.IDE0021.severity = silent
+csharp_style_expression_bodied_indexers = true:silent
+# IDE0026: Use expression body for indexers
+dotnet_diagnostic.IDE0026.severity = silent
+csharp_style_expression_bodied_lambdas = true:silent
+# IDE0053: Use expression body for lambdas
+dotnet_diagnostic.IDE0053.severity = silent
+csharp_style_expression_bodied_local_functions = false:silent
+# IDE0061: Use expression body for local functions
+dotnet_diagnostic.IDE0061.severity = silent
+csharp_style_expression_bodied_methods = false:silent
+# IDE0022: Use expression body for methods
+dotnet_diagnostic.IDE0022.severity = silent
+csharp_style_expression_bodied_operators = false:silent
+# IDE0023: Use expression body for conversion operators
+dotnet_diagnostic.IDE0023.severity = silent
+# IDE0024: Use expression body for operators
+dotnet_diagnostic.IDE0024.severity = silent
+csharp_style_expression_bodied_properties = true:silent
+# IDE0025: Use expression body for properties
+dotnet_diagnostic.IDE0025.severity = silent
+
+# Member preferences (these analyzers are unreliable)
+# IDE0051: Remove unused private member
+dotnet_diagnostic.IDE0051.severity = silent
+# IDE0052: Remove unread private member
+dotnet_diagnostic.IDE0052.severity = silent
# Code-block preferences
-csharp_prefer_braces = true:suggestion
+csharp_prefer_braces = true:silent
+# IDE0011: Add braces
+dotnet_diagnostic.IDE0011.severity = silent
# Indentation preferences
csharp_indent_case_contents_when_block = false
@@ -66,19 +131,44 @@ csharp_indent_case_contents_when_block = false
csharp_preserve_single_line_statements = false
# 'var' usage preferences
-csharp_style_var_for_built_in_types = false:none
-csharp_style_var_when_type_is_apparent = true:none
-csharp_style_var_elsewhere = false:none
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = true:silent
+csharp_style_var_elsewhere = false:silent
+# IDE0007: Use var instead of explicit type
+dotnet_diagnostic.IDE0007.severity = silent
+# IDE0008: Use explicit type instead of var
+dotnet_diagnostic.IDE0008.severity = silent
# Parentheses preferences
-dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
-dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion
-
-# Expression value is never used
-dotnet_diagnostic.IDE0058.severity = none
-
-#### Naming Style ####
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent
+# IDE0047: Remove unnecessary parentheses
+dotnet_diagnostic.IDE0047.severity = silent
+# IDE0048: Add parentheses for clarity
+dotnet_diagnostic.IDE0048.severity = silent
+
+# Switch preferences
+# IDE0010: Add missing cases to switch statement
+dotnet_diagnostic.IDE0010.severity = silent
+# IDE0072: Add missing cases to switch expression
+dotnet_diagnostic.IDE0072.severity = silent
+
+# Null check preferences
+# IDE0029: Null check can be simplified
+dotnet_diagnostic.IDE0029.severity = silent
+# IDE0030: Null check can be simplified
+dotnet_diagnostic.IDE0030.severity = silent
+# IDE0270: Null check can be simplified
+dotnet_diagnostic.IDE0270.severity = silent
+
+# JSON002: Probable JSON string detected
+dotnet_diagnostic.JSON002.severity = silent
+
+# CA1062: Validate arguments of public methods
+dotnet_code_quality.CA1062.excluded_symbol_names = Accept|DefaultVisit|Visit*|Apply*
+
+#### .NET Naming Style ####
dotnet_diagnostic.IDE1006.severity = warning
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..0c78db34f3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+# When running OpenAPI tests, these committed files are downloaded and written to disk (so we'll know when something changes).
+# On Windows, these text files are auto-converted to crlf on git fetch, while the written downloaded files use lf line endings.
+# Therefore, running the tests on Windows creates local changes. Staging them auto-converts back to crlf, which undoes the changes.
+# To avoid this annoyance, the next line opts out of the auto-conversion and forces line endings to lf.
+**/GeneratedSwagger/**/*.json text eol=lf
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index e7d3ce2494..14b5da9852 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -22,7 +22,7 @@ Bugs are tracked as [GitHub issues](https://github.com/json-api-dotnet/JsonApiDo
Explain the problem and include additional details to help maintainers reproduce the problem:
- **Use a clear and descriptive title** for the issue to identify the problem.
-- **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, don't just say what you did, but explain how you did it.
+- **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, don't just say what you did, but explain how you did it.
- **Provide specific examples to demonstrate the steps.** Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://docs.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks).
- **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. Explain which behavior you expected to see instead and why.
- **If you're reporting a crash**, include the full exception stack trace.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index aa52764416..beb6e779ed 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,24 +1,24 @@
version: 2
updates:
-- package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "weekly"
- pull-request-branch-name:
- separator: "-"
-- package-ecosystem: nuget
- directory: "/"
- schedule:
- interval: daily
- pull-request-branch-name:
- separator: "-"
- open-pull-requests-limit: 25
- ignore:
- # Block updates to all exposed dependencies of the NuGet packages we produce, as updating them would be a breaking change.
- - dependency-name: 'Ben.Demystifier'
- - dependency-name: 'Humanizer*'
- - dependency-name: 'Microsoft.CodeAnalysis*'
- - dependency-name: 'Microsoft.EntityFrameworkCore*'
- # Block major updates of packages that require a matching .NET version.
- - dependency-name: 'Microsoft.AspNetCore*'
- update-types: ["version-update:semver-major"]
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ pull-request-branch-name:
+ separator: "-"
+ - package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: daily
+ pull-request-branch-name:
+ separator: "-"
+ open-pull-requests-limit: 25
+ ignore:
+ # Block updates to all exposed dependencies of the NuGet packages we produce, as updating them would be a breaking change.
+ - dependency-name: "Ben.Demystifier"
+ - dependency-name: "Humanizer*"
+ - dependency-name: "Microsoft.CodeAnalysis*"
+ - dependency-name: "Microsoft.EntityFrameworkCore*"
+ # Block major updates of packages that require a matching .NET version.
+ - dependency-name: "Microsoft.AspNetCore*"
+ update-types: ["version-update:semver-major"]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index da0993c185..7ee1333bd6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,7 +21,7 @@ concurrency:
cancel-in-progress: true
env:
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
@@ -46,8 +46,8 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.0.x
- 8.0.x
+ 8.0.*
+ 9.0.*
- name: Show installed versions
shell: pwsh
run: |
@@ -72,14 +72,14 @@ jobs:
$versionSuffix = $segments.Length -eq 1 ? '' : $segments[1..$($segments.Length - 1)] -join '-'
[xml]$xml = Get-Content Directory.Build.props
- $configuredVersionPrefix = $xml.Project.PropertyGroup.JsonApiDotNetCoreVersionPrefix | Select-Object -First 1
+ $configuredVersionPrefix = $xml.Project.PropertyGroup.VersionPrefix | Select-Object -First 1
if ($configuredVersionPrefix -ne $versionPrefix) {
Write-Error "Version prefix from git release tag '$versionPrefix' does not match version prefix '$configuredVersionPrefix' stored in Directory.Build.props."
# To recover from this:
# - Delete the GitHub release
# - Run: git push --delete origin the-invalid-tag-name
- # - Adjust JsonApiDotNetCoreVersionPrefix in Directory.Build.props, commit and push
+ # - Adjust VersionPrefix in Directory.Build.props, commit and push
# - Recreate the GitHub release
}
}
@@ -110,7 +110,7 @@ jobs:
if: matrix.os == 'ubuntu-latest'
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- uses: codecov/codecov-action@v4
+ uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
verbose: true
@@ -132,7 +132,7 @@ jobs:
run: |
cd docs
& ./generate-examples.ps1
- dotnet docfx docfx.json
+ dotnet docfx docfx.json --warningsAsErrors true
if ($LastExitCode -ne 0) {
Write-Error "docfx failed with exit code $LastExitCode."
}
@@ -164,8 +164,8 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.0.x
- 8.0.x
+ 8.0.*
+ 9.0.*
- name: Git checkout
uses: actions/checkout@v4
- name: Restore tools
@@ -219,8 +219,8 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.0.x
- 8.0.x
+ 8.0.*
+ 9.0.*
- name: Git checkout
uses: actions/checkout@v4
with:
@@ -284,4 +284,4 @@ jobs:
NUGET_ORG_API_KEY: ${{ secrets.NUGET_ORG_API_KEY }}
shell: pwsh
run: |
- dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:NUGET_ORG_API_KEY" --source 'nuget.org'
+ dotnet nuget push "$env:GITHUB_WORKSPACE/packages/*.nupkg" --api-key "$env:NUGET_ORG_API_KEY" --source 'nuget.org' --skip-duplicate
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index eb0375769e..508d210158 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -27,14 +27,17 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.0.x
- 8.0.x
+ 8.0.*
+ 9.0.*
- name: Git checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
+ - name: Restore .NET tools
+ run: |
+ dotnet tool restore
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
diff --git a/Build.ps1 b/Build.ps1
index 3abc926e6a..6c6ff9c13a 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -1,5 +1,3 @@
-$versionSuffix="pre"
-
function VerifySuccessExitCode {
if ($LastExitCode -ne 0) {
throw "Command failed with exit code $LastExitCode."
@@ -16,14 +14,14 @@ Remove-Item -Recurse -Force * -Include coverage.cobertura.xml
dotnet tool restore
VerifySuccessExitCode
-dotnet build --configuration Release /p:VersionSuffix=$versionSuffix
+dotnet build --configuration Release
VerifySuccessExitCode
-dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage"
+dotnet test --no-build --configuration Release --verbosity quiet --collect:"XPlat Code Coverage"
VerifySuccessExitCode
dotnet reportgenerator -reports:**\coverage.cobertura.xml -targetdir:artifacts\coverage -filefilters:-*.g.cs
VerifySuccessExitCode
-dotnet pack --no-build --configuration Release --output artifacts/packages /p:VersionSuffix=$versionSuffix
+dotnet pack --no-build --configuration Release --output artifacts/packages
VerifySuccessExitCode
diff --git a/CSharpGuidelinesAnalyzer.config b/CSharpGuidelinesAnalyzer.config
index 89b568e155..6d5453159a 100644
--- a/CSharpGuidelinesAnalyzer.config
+++ b/CSharpGuidelinesAnalyzer.config
@@ -1,5 +1,5 @@
-
+
diff --git a/CodingGuidelines.ruleset b/CodingGuidelines.ruleset
index e647ad9e58..b29d7423b4 100644
--- a/CodingGuidelines.ruleset
+++ b/CodingGuidelines.ruleset
@@ -1,32 +1,54 @@
-
+
+
+
-
-
-
-
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 860217f52e..05d57e58b4 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,4 +1,36 @@
+
+ enable
+ latest
+ enable
+ false
+ false
+ true
+ Recommended
+ $(MSBuildThisFileDirectory)CodingGuidelines.ruleset
+ $(MSBuildThisFileDirectory)tests.runsettings
+ 5.7.0
+ pre
+ 1
+ direct
+ $(NoWarn);NETSDK1215
+
+
+
+
+ IDE0028;IDE0300;IDE0301;IDE0302;IDE0303;IDE0304;IDE0305;IDE0306
+ $(NoWarn);$(UseCollectionExpressionRules)
+
+
+ $(NoWarn);NETSDK1215
+
+
$(NoWarn);AV2210
@@ -13,20 +45,18 @@
true
+
+ $(NoWarn);CA1707;CA1062
+
+
+
+ $(NoWarn);CA1062
+
+
-
+
-
-
- enable
- latest
- enable
- false
- false
- $(MSBuildThisFileDirectory)CodingGuidelines.ruleset
- $(MSBuildThisFileDirectory)tests.runsettings
- 5.6.0
-
diff --git a/JsonApiDotNetCore.sln b/JsonApiDotNetCore.sln
index c555610364..793c01950d 100644
--- a/JsonApiDotNetCore.sln
+++ b/JsonApiDotNetCore.sln
@@ -62,6 +62,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperExample", "src\Exampl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DapperTests", "test\DapperTests\DapperTests.csproj", "{80E322F5-5F5D-4670-A30F-02D33C2C7900}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.OpenApi.Swashbuckle", "src\JsonApiDotNetCore.OpenApi.Swashbuckle\JsonApiDotNetCore.OpenApi.Swashbuckle.csproj", "{71287D6F-6C3B-44B4-9FCA-E78FE3F02289}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiTests", "test\OpenApiTests\OpenApiTests.csproj", "{B693DE14-BB28-496F-AB39-B4E674ABCA80}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.OpenApi.Client.NSwag", "src\JsonApiDotNetCore.OpenApi.Client.NSwag\JsonApiDotNetCore.OpenApi.Client.NSwag.csproj", "{5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiNSwagClientExample", "src\Examples\OpenApiNSwagClientExample\OpenApiNSwagClientExample.csproj", "{7FC5DFA3-6F66-4FD8-820D-81E93856F252}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiNSwagClientTests", "test\OpenApiNSwagClientTests\OpenApiNSwagClientTests.csproj", "{77F98215-3085-422E-B99D-4C404C2114CF}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiNSwagEndToEndTests", "test\OpenApiNSwagEndToEndTests\OpenApiNSwagEndToEndTests.csproj", "{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.OpenApi.Client.Kiota", "src\JsonApiDotNetCore.OpenApi.Client.Kiota\JsonApiDotNetCore.OpenApi.Client.Kiota.csproj", "{617FCA5D-A2DE-4083-B373-ADCA9901059F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiKiotaClientExample", "src\Examples\OpenApiKiotaClientExample\OpenApiKiotaClientExample.csproj", "{39DEAFE8-AE29-48E5-A67D-73776D70FC82}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiKiotaEndToEndTests", "test\OpenApiKiotaEndToEndTests\OpenApiKiotaEndToEndTests.csproj", "{FD86C676-3D80-4971-8D8C-B0729B2251F6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -312,6 +330,114 @@ Global
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x64.Build.0 = Release|Any CPU
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x86.ActiveCfg = Release|Any CPU
{80E322F5-5F5D-4670-A30F-02D33C2C7900}.Release|x86.Build.0 = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x64.Build.0 = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Debug|x86.Build.0 = Debug|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|Any CPU.Build.0 = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.ActiveCfg = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x64.Build.0 = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.ActiveCfg = Release|Any CPU
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289}.Release|x86.Build.0 = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x64.Build.0 = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Debug|x86.Build.0 = Debug|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.ActiveCfg = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x64.Build.0 = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.ActiveCfg = Release|Any CPU
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80}.Release|x86.Build.0 = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|x64.Build.0 = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Debug|x86.Build.0 = Debug|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|x64.ActiveCfg = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|x64.Build.0 = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|x86.ActiveCfg = Release|Any CPU
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C}.Release|x86.Build.0 = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|x64.Build.0 = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Debug|x86.Build.0 = Debug|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|x64.ActiveCfg = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|x64.Build.0 = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|x86.ActiveCfg = Release|Any CPU
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252}.Release|x86.Build.0 = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|x64.Build.0 = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Debug|x86.Build.0 = Debug|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|x64.ActiveCfg = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|x64.Build.0 = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|x86.ActiveCfg = Release|Any CPU
+ {77F98215-3085-422E-B99D-4C404C2114CF}.Release|x86.Build.0 = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|x64.Build.0 = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Debug|x86.Build.0 = Debug|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x64.ActiveCfg = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x64.Build.0 = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x86.ActiveCfg = Release|Any CPU
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x86.Build.0 = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x64.Build.0 = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x86.Build.0 = Debug|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x64.ActiveCfg = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x64.Build.0 = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x86.ActiveCfg = Release|Any CPU
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x86.Build.0 = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x64.Build.0 = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x86.Build.0 = Debug|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|Any CPU.Build.0 = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x64.ActiveCfg = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x64.Build.0 = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x86.ActiveCfg = Release|Any CPU
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x86.Build.0 = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x64.Build.0 = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x86.Build.0 = Debug|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x64.ActiveCfg = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x64.Build.0 = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x86.ActiveCfg = Release|Any CPU
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -337,6 +463,15 @@ Global
{24B0C12F-38CD-4245-8785-87BEFAD55B00} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{C1774117-5073-4DF8-B5BE-BF7B538BD1C2} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{80E322F5-5F5D-4670-A30F-02D33C2C7900} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {71287D6F-6C3B-44B4-9FCA-E78FE3F02289} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
+ {B693DE14-BB28-496F-AB39-B4E674ABCA80} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {5ADAA902-5A75-4ECB-B4B4-03291D63CE9C} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
+ {7FC5DFA3-6F66-4FD8-820D-81E93856F252} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
+ {77F98215-3085-422E-B99D-4C404C2114CF} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {3BA4F9B9-3D90-44B5-B09C-28D98E0B4225} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
+ {617FCA5D-A2DE-4083-B373-ADCA9901059F} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
+ {39DEAFE8-AE29-48E5-A67D-73776D70FC82} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
+ {FD86C676-3D80-4971-8D8C-B0729B2251F6} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}
diff --git a/JsonApiDotNetCore.sln.DotSettings b/JsonApiDotNetCore.sln.DotSettings
index 399be60883..6c29a58aef 100644
--- a/JsonApiDotNetCore.sln.DotSettings
+++ b/JsonApiDotNetCore.sln.DotSettings
@@ -1,19 +1,12 @@
- // Use the following placeholders:
-// $EXPR$ -- source expression
-// $NAME$ -- source name (string literal or 'nameof' expression)
-// $MESSAGE$ -- string literal in the form of "$NAME$ != null"
-JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
- 199
- 5000
- 99
- 100
- 200
- 1000
- 500
+ 5000
+ 20003000
- 50False
+ True
+ 83FF097C-C8C6-477B-9FAB-DF99B84978B5/f:ReadOnlySet.cs
+ swagger.g.json
+ swagger.jsonSOLUTIONTrueTrue
@@ -81,6 +74,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
SUGGESTIONSUGGESTIONWARNING
+ DO_NOT_SHOWWARNINGWARNINGWARNING
@@ -99,6 +93,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
WARNINGTrueSUGGESTION
+ False<?xml version="1.0" encoding="utf-16"?><Profile name="JADNC Full Cleanup"><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" ArrangeNamespaces="True" ArrangeNullCheckingPattern="True" /><CssAlphabetizeProperties>True</CssAlphabetizeProperties><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs><OptimizeReferenceCommentsTs>True</OptimizeReferenceCommentsTs><PublicModifierStyleTs>True</PublicModifierStyleTs><ExplicitAnyTs>True</ExplicitAnyTs><TypeAnnotationStyleTs>True</TypeAnnotationStyleTs><RelativePathStyleTs>True</RelativePathStyleTs><AsInsteadOfCastTs>True</AsInsteadOfCastTs><HtmlReformatCode>True</HtmlReformatCode><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CssReformatCode>True</CssReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSReformatInactiveBranches>True</CSReformatInactiveBranches></Profile>JADNC Full CleanupRequired
@@ -137,6 +132,7 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
NEVERFalseNEVER
+ FalseFalseFalseNEVER
@@ -595,11 +591,12 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
TrueTrueTrue
+ TrueTrueTrueTrueTrue
- Replace argument null check using throw expression with Guard clause
+ Replace argument null check using throw expression with ArgumentNullException.ThrowIfNullTrueTrueFalse
@@ -618,13 +615,12 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$);
TrueCSHARPFalse
- Replace argument null check with Guard clause
- JsonApiDotNetCore.ArgumentGuard.NotNull($argument$);
+ System.ArgumentNullException.ThrowIfNull($argument$);
$left$ = $right$;$left$ = $right$ ?? throw new ArgumentNullException(nameof($argument$));WARNINGTrue
- Replace argument == null check with Guard clause
+ Replace argument == null check with ArgumentNullException.ThrowIfNullTrueTrueFalse
@@ -633,8 +629,7 @@ $left$ = $right$;TrueCSHARPFalse
- Replace argument null check with Guard clause
- JsonApiDotNetCore.ArgumentGuard.NotNull($argument$);
+ System.ArgumentNullException.ThrowIfNull($argument$);if ($argument$ == null) throw new ArgumentNullException(nameof($argument$));WARNINGTrue
@@ -646,12 +641,11 @@ $left$ = $right$;
TrueCSHARPFalse
- Replace collection null/empty check with extension method$collection$.IsNullOrEmpty()$collection$ == null || !$collection$.Any()WARNINGTrue
- Replace argument is null check with Guard clause
+ Replace argument is null check with ArgumentNullException.ThrowIfNullTrueTrueFalse
@@ -660,15 +654,17 @@ $left$ = $right$;
TrueCSHARPFalse
- JsonApiDotNetCore.ArgumentGuard.NotNull($argument$);
+ System.ArgumentNullException.ThrowIfNull($argument$);if ($argument$ is null) throw new ArgumentNullException(nameof($argument$));WARNINGTrue
+ TrueTrueTrueTrueTrueTrue
+ TrueTrueTrueTrue
diff --git a/PackageReadme.md b/PackageReadme.md
index f57386041b..a6d0017f6a 100644
--- a/PackageReadme.md
+++ b/PackageReadme.md
@@ -1,4 +1,4 @@
-A framework for building [JSON:API](http://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/).
+A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/).
The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy.
diff --git a/README.md b/README.md
index 5ae9184e0c..d01f0b2dc7 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
# JsonApiDotNetCore
-A framework for building [JSON:API](http://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/).
+A framework for building [JSON:API](https://jsonapi.org/) compliant REST APIs using .NET Core and Entity Framework Core. Includes support for [Atomic Operations](https://jsonapi.org/ext/atomic/).
[](https://github.com/json-api-dotnet/JsonApiDotNetCore/actions/workflows/build.yml?query=branch%3Amaster)
[](https://codecov.io/gh/json-api-dotnet/JsonApiDotNetCore)
[](https://www.nuget.org/packages/JsonApiDotNetCore/)
[](https://gitter.im/json-api-dotnet-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](http://www.firsttimersonly.com/)
+[](https://www.firsttimersonly.com/)
The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection, making extensibility incredibly easy.
@@ -85,13 +85,10 @@ See also our [versioning policy](./VERSIONING_POLICY.md).
| | | 7 | 7 |
| 5.5+ | Stable | 6 | 6, 7 |
| | | 7 | 7 |
-| | | 8 | 8 |
-| master | Preview | 6 | 6, 7 |
-| | | 7 | 7 |
-| | | 8 | 8 |
-| openapi | Experimental | 6 | 6, 7 |
-| | | 7 | 7 |
-| | | 8 | 8 |
+| | | 8 | 8, 9 |
+| | | 9 | 9 |
+| master | Preview | 8 | 8, 9 |
+| | | 9 | 9 |
## Contributing
@@ -110,7 +107,7 @@ To try it out, follow the steps below:
In the command above:
- Replace YOUR-GITHUB-USERNAME with the username you use to login your GitHub account.
- Replace YOUR-PAT-CLASSIC with the token your created above.
-
+
:warning: If the above command doesn't give you access in the next step, remove the package source by running:
```bash
dotnet nuget remove source github-json-api
diff --git a/ROADMAP.md b/ROADMAP.md
index 7ba6a2a5a8..d73797824f 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -6,12 +6,14 @@ This document provides an overview of the direction this project is heading and
We have an interest in the following topics. It's too soon yet to decide whether they'll make it into v5.x or in a later major version.
-- OpenAPI (Swagger): Generate documentation and typed clients [#1046](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1046)
+- Provide additional OpenAPI support [#1046](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1046)
- Query strings on JSON-mapped columns [#1439](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1439)
+- Improved resource inheritance [#1642](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1642)
- Improved SQL Server support [#1118](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1118)
+- Use incremental source generator [#1447](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1447)
- Optimistic concurrency [#1119](https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1119)
-- Fluent API [#776](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/776)
- Idempotency [#1132](https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1132)
+- Fluent API [#776](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/776)
## Feedback
diff --git a/WarningSeverities.DotSettings b/WarningSeverities.DotSettings
index 060df315df..5b64971520 100644
--- a/WarningSeverities.DotSettings
+++ b/WarningSeverities.DotSettings
@@ -1,4 +1,5 @@
+ WARNINGWARNINGWARNINGWARNING
@@ -197,7 +198,6 @@
WARNINGWARNINGWARNING
- WARNINGWARNINGWARNINGWARNING
diff --git a/benchmarks/Benchmarks.csproj b/benchmarks/Benchmarks.csproj
index 9dbb9ba093..90d53461d2 100644
--- a/benchmarks/Benchmarks.csproj
+++ b/benchmarks/Benchmarks.csproj
@@ -1,7 +1,7 @@
Exe
- net8.0
+ net9.0true
diff --git a/benchmarks/Deserialization/DeserializationBenchmarkBase.cs b/benchmarks/Deserialization/DeserializationBenchmarkBase.cs
index bbf746d1a8..4febabba1a 100644
--- a/benchmarks/Deserialization/DeserializationBenchmarkBase.cs
+++ b/benchmarks/Deserialization/DeserializationBenchmarkBase.cs
@@ -11,10 +11,12 @@
namespace Benchmarks.Deserialization;
-public abstract class DeserializationBenchmarkBase
+public abstract class DeserializationBenchmarkBase : IDisposable
{
- protected readonly JsonSerializerOptions SerializerReadOptions;
- protected readonly DocumentAdapter DocumentAdapter;
+ private readonly ServiceContainer _serviceProvider = new();
+
+ protected JsonSerializerOptions SerializerReadOptions { get; }
+ protected DocumentAdapter DocumentAdapter { get; }
protected DeserializationBenchmarkBase()
{
@@ -23,12 +25,11 @@ protected DeserializationBenchmarkBase()
options.SerializerOptions.Converters.Add(new ResourceObjectConverter(resourceGraph));
SerializerReadOptions = ((IJsonApiOptions)options).SerializerReadOptions;
- var serviceContainer = new ServiceContainer();
- var resourceFactory = new ResourceFactory(serviceContainer);
- var resourceDefinitionAccessor = new ResourceDefinitionAccessor(resourceGraph, serviceContainer);
+ var resourceFactory = new ResourceFactory(_serviceProvider);
+ var resourceDefinitionAccessor = new ResourceDefinitionAccessor(resourceGraph, _serviceProvider);
- serviceContainer.AddService(typeof(IResourceDefinitionAccessor), resourceDefinitionAccessor);
- serviceContainer.AddService(typeof(IResourceDefinition), new JsonApiResourceDefinition(resourceGraph));
+ _serviceProvider.AddService(typeof(IResourceDefinitionAccessor), resourceDefinitionAccessor);
+ _serviceProvider.AddService(typeof(IResourceDefinition), new JsonApiResourceDefinition(resourceGraph));
// ReSharper disable once VirtualMemberCallInConstructor
JsonApiRequest request = CreateJsonApiRequest(resourceGraph);
@@ -53,6 +54,22 @@ protected DeserializationBenchmarkBase()
protected abstract JsonApiRequest CreateJsonApiRequest(IResourceGraph resourceGraph);
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+#pragma warning disable CA1063 // Implement IDisposable Correctly
+ private void Dispose(bool disposing)
+#pragma warning restore CA1063 // Implement IDisposable Correctly
+ {
+ if (disposing)
+ {
+ _serviceProvider.Dispose();
+ }
+ }
+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class IncomingResource : Identifiable
{
diff --git a/benchmarks/Program.cs b/benchmarks/Program.cs
index b1e3307931..04d5fa1eaa 100644
--- a/benchmarks/Program.cs
+++ b/benchmarks/Program.cs
@@ -3,20 +3,12 @@
using Benchmarks.QueryString;
using Benchmarks.Serialization;
-namespace Benchmarks;
+var switcher = new BenchmarkSwitcher([
+ typeof(ResourceDeserializationBenchmarks),
+ typeof(OperationsDeserializationBenchmarks),
+ typeof(ResourceSerializationBenchmarks),
+ typeof(OperationsSerializationBenchmarks),
+ typeof(QueryStringParserBenchmarks)
+]);
-internal static class Program
-{
- private static void Main(string[] args)
- {
- var switcher = new BenchmarkSwitcher([
- typeof(ResourceDeserializationBenchmarks),
- typeof(OperationsDeserializationBenchmarks),
- typeof(ResourceSerializationBenchmarks),
- typeof(OperationsSerializationBenchmarks),
- typeof(QueryStringParserBenchmarks)
- ]);
-
- switcher.Run(args);
- }
-}
+switcher.Run(args);
diff --git a/benchmarks/QueryString/QueryStringParserBenchmarks.cs b/benchmarks/QueryString/QueryStringParserBenchmarks.cs
index 0b2f88134a..5e5a65ed9f 100644
--- a/benchmarks/QueryString/QueryStringParserBenchmarks.cs
+++ b/benchmarks/QueryString/QueryStringParserBenchmarks.cs
@@ -14,8 +14,9 @@ namespace Benchmarks.QueryString;
[MarkdownExporter]
[SimpleJob(3, 10, 20)]
[MemoryDiagnoser]
-public class QueryStringParserBenchmarks
+public class QueryStringParserBenchmarks : IDisposable
{
+ private readonly ServiceContainer _serviceProvider = new();
private readonly FakeRequestQueryStringAccessor _queryStringAccessor = new();
private readonly QueryStringReader _queryStringReader;
@@ -30,11 +31,11 @@ public QueryStringParserBenchmarks()
var request = new JsonApiRequest
{
- PrimaryResourceType = resourceGraph.GetResourceType(typeof(QueryableResource)),
+ PrimaryResourceType = resourceGraph.GetResourceType(),
IsCollection = true
};
- var resourceFactory = new ResourceFactory(new ServiceContainer());
+ var resourceFactory = new ResourceFactory(_serviceProvider);
var includeParser = new IncludeParser(options);
var includeReader = new IncludeQueryStringParameterReader(includeParser, request, resourceGraph);
@@ -92,4 +93,20 @@ public void ComplexQuery()
_queryStringAccessor.SetQueryString(queryString);
_queryStringReader.ReadAll(null);
}
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+#pragma warning disable CA1063 // Implement IDisposable Correctly
+ private void Dispose(bool disposing)
+#pragma warning restore CA1063 // Implement IDisposable Correctly
+ {
+ if (disposing)
+ {
+ _serviceProvider.Dispose();
+ }
+ }
}
diff --git a/benchmarks/Serialization/OperationsSerializationBenchmarks.cs b/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
index 458c4eecae..8c4a00b6da 100644
--- a/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
+++ b/benchmarks/Serialization/OperationsSerializationBenchmarks.cs
@@ -13,7 +13,7 @@ namespace Benchmarks.Serialization;
// ReSharper disable once ClassCanBeSealed.Global
public class OperationsSerializationBenchmarks : SerializationBenchmarkBase
{
- private readonly IEnumerable _responseOperations;
+ private readonly List _responseOperations;
public OperationsSerializationBenchmarks()
{
@@ -23,7 +23,7 @@ public OperationsSerializationBenchmarks()
_responseOperations = CreateResponseOperations(request);
}
- private static IEnumerable CreateResponseOperations(IJsonApiRequest request)
+ private static List CreateResponseOperations(IJsonApiRequest request)
{
var resource1 = new OutgoingResource
{
@@ -102,14 +102,14 @@ private static IEnumerable CreateResponseOperations(IJsonApi
var targetedFields = new TargetedFields();
- return new List
- {
- new(resource1, targetedFields, request),
- new(resource2, targetedFields, request),
- new(resource3, targetedFields, request),
- new(resource4, targetedFields, request),
- new(resource5, targetedFields, request)
- };
+ return
+ [
+ new OperationContainer(resource1, targetedFields, request),
+ new OperationContainer(resource2, targetedFields, request),
+ new OperationContainer(resource3, targetedFields, request),
+ new OperationContainer(resource4, targetedFields, request),
+ new OperationContainer(resource5, targetedFields, request)
+ ];
}
[Benchmark]
diff --git a/benchmarks/Serialization/ResourceSerializationBenchmarks.cs b/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
index a2d76b87b1..6f979e86b9 100644
--- a/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
+++ b/benchmarks/Serialization/ResourceSerializationBenchmarks.cs
@@ -1,7 +1,6 @@
using System.Collections.Immutable;
using System.Text.Json;
using BenchmarkDotNet.Attributes;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Queries;
@@ -97,12 +96,17 @@ private static OutgoingResource CreateResponseResource()
resource1.Single2 = resource2;
resource2.Single3 = resource3;
- resource3.Multi4 = resource4.AsHashSet();
- resource4.Multi5 = resource5.AsHashSet();
+ resource3.Multi4 = ToHashSet(resource4);
+ resource4.Multi5 = ToHashSet(resource5);
return resource1;
}
+ private static HashSet ToHashSet(T element)
+ {
+ return [element];
+ }
+
[Benchmark]
public string SerializeResourceResponse()
{
diff --git a/benchmarks/Serialization/SerializationBenchmarkBase.cs b/benchmarks/Serialization/SerializationBenchmarkBase.cs
index eba222c9d1..c8451835cc 100644
--- a/benchmarks/Serialization/SerializationBenchmarkBase.cs
+++ b/benchmarks/Serialization/SerializationBenchmarkBase.cs
@@ -14,9 +14,9 @@ namespace Benchmarks.Serialization;
public abstract class SerializationBenchmarkBase
{
- protected readonly JsonSerializerOptions SerializerWriteOptions;
- protected readonly IResponseModelAdapter ResponseModelAdapter;
- protected readonly IResourceGraph ResourceGraph;
+ protected JsonSerializerOptions SerializerWriteOptions { get; }
+ protected IResponseModelAdapter ResponseModelAdapter { get; }
+ protected IResourceGraph ResourceGraph { get; }
protected SerializationBenchmarkBase()
{
diff --git a/docs/build-dev.ps1 b/docs/build-dev.ps1
index d65826687a..6345875fc7 100644
--- a/docs/build-dev.ps1
+++ b/docs/build-dev.ps1
@@ -40,7 +40,7 @@ if (-Not $NoBuild -Or -Not (Test-Path -Path _site)) {
dotnet tool restore
VerifySuccessExitCode
-dotnet docfx ./docfx.json
+dotnet docfx ./docfx.json --warningsAsErrors true
VerifySuccessExitCode
Copy-Item -Force home/*.html _site/
diff --git a/docs/docfx.json b/docs/docfx.json
index eb94da412e..232d8768eb 100644
--- a/docs/docfx.json
+++ b/docs/docfx.json
@@ -1,53 +1,68 @@
{
- "metadata": [
+ "metadata": [
+ {
+ "properties": {
+ "ProduceReferenceAssembly": "true"
+ },
+ "src": [
{
- "src": [
- {
- "files": [ "**/JsonApiDotNetCore.csproj","**/JsonApiDotNetCore.Annotations.csproj" ],
- "src": "../"
- }
- ],
- "dest": "api",
- "disableGitFeatures": false
+ "files": [
+ "**/JsonApiDotNetCore.csproj",
+ "**/JsonApiDotNetCore.Annotations.csproj"
+ ],
+ "src": "../"
}
- ],
- "build": {
- "content": [
- {
- "files": [
- "api/**.yml",
- "api/index.md",
- "getting-started/**.md",
- "getting-started/**/toc.yml",
- "usage/**.md",
- "request-examples/**.md",
- "internals/**.md",
- "toc.yml",
- "*.md"
- ],
- "exclude": [
- "**/README.md"
- ]
- }
- ],
- "resource": [
- {
- "files": [ "diagrams/*.svg" ]
- }
- ],
- "overwrite": [
- {
- "exclude": [ "obj/**", "_site/**" ]
- }
- ],
- "dest": "_site",
- "globalMetadataFiles": [],
- "fileMetadataFiles": [],
- "template": [ "default", "modern" ],
- "postProcessors": [],
- "noLangKeyword": false,
- "keepFileLink": false,
- "cleanupCacheHistory": false,
- "disableGitFeatures": false
+ ],
+ "dest": "api",
+ "disableGitFeatures": false
}
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md",
+ "ext/openapi/index.md",
+ "getting-started/**.md",
+ "getting-started/**/toc.yml",
+ "usage/**.md",
+ "request-examples/**.md",
+ "internals/**.md",
+ "toc.yml",
+ "*.md"
+ ],
+ "exclude": [
+ "**/README.md"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "diagrams/*.svg"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "dest": "_site",
+ "globalMetadataFiles": [],
+ "fileMetadataFiles": [],
+ "template": [
+ "default",
+ "modern"
+ ],
+ "postProcessors": [],
+ "noLangKeyword": false,
+ "keepFileLink": false,
+ "cleanupCacheHistory": false,
+ "disableGitFeatures": false
+ }
}
diff --git a/docs/ext/openapi/index.md b/docs/ext/openapi/index.md
new file mode 100644
index 0000000000..20aad7b305
--- /dev/null
+++ b/docs/ext/openapi/index.md
@@ -0,0 +1,127 @@
+# JSON:API Extension for OpenAPI
+
+This extension facilitates using OpenAPI client generators targeting JSON:API documents.
+
+In JSON:API, a resource object contains the `type` member, which defines the structure of nested [attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects.
+While OpenAPI supports such constraints using `allOf` inheritance with a discriminator property for the `data` member,
+it provides no way to express that the discriminator recursively applies to nested objects.
+
+This extension addresses that limitation by defining additional discriminator properties to guide code generation tools.
+
+## URI
+
+This extension has the URI `https://www.jsonapi.net/ext/openapi`.
+Because code generators often choke on the double quotes in `Accept` and `Content-Type` HTTP header values, a relaxed form is also permitted: `openapi`.
+
+For example, the following `Content-Type` header:
+
+```http
+Content-Type: application/vnd.api+json; ext="https://www.jsonapi.net/ext/openapi"
+```
+
+is equivalent to:
+
+```http
+Content-Type: application/vnd.api+json; ext=openapi
+```
+
+To avoid the need for double quotes when multiple extensions are used, the following relaxed form can be used:
+
+```http
+Content-Type: application/vnd.api+json; ext=openapi; ext=atomic
+```
+
+> [!NOTE]
+> The [base specification](https://jsonapi.org/format/#media-type-parameter-rules) *forbids* the use of multiple `ext` parameters
+> and *requires* that each extension name must be a URI.
+> This extension relaxes both constraints for practical reasons, to workaround bugs in client generators that produce broken code otherwise.
+
+## Namespace
+
+This extension uses the namespace `openapi`.
+
+> [!NOTE]
+> JSON:API extensions can only introduce new document members using a reserved namespace as a prefix.
+
+## Document Structure
+
+A document that supports this extension MAY include any of the top-level members allowed by the base specification,
+including any members defined in the [Atomic Operations extension](https://jsonapi.org/ext/atomic/).
+
+### Resource Objects
+
+In addition to the members allowed by the base specification, the following member MAY be included
+in [attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects:
+
+* `openapi:discriminator` - A string that MUST be identical to the `type` member in the containing [resource object](https://jsonapi.org/format/#document-resource-objects).
+
+Here's how an article (i.e. a resource of type "articles") might appear in a document:
+
+```json
+{
+ "data": {
+ "type": "articles",
+ "id": "1",
+ "attributes": {
+ "openapi:discriminator": "articles",
+ "title": "Rails is Omakase"
+ },
+ "relationships": {
+ "openapi:discriminator": "articles",
+ "author": {
+ "data": { "type": "people", "id": "9" }
+ }
+ }
+ }
+}
+```
+
+### Atomic Operations
+
+In addition to the members allowed by the [Atomic Operations extension](https://jsonapi.org/ext/atomic/),
+the following member MAY be included in elements of an `atomic:operations` array:
+
+* `openapi:discriminator` - A free-format string to facilitate generation of client code.
+
+For example:
+
+```http
+POST /operations HTTP/1.1
+Host: example.org
+Content-Type: application/vnd.api+json; ext="https://www.jsonapi.net/ext/openapi https://jsonapi.org/ext/atomic"
+Accept: application/vnd.api+json; ext="https://www.jsonapi.net/ext/openapi https://jsonapi.org/ext/atomic"
+
+{
+ "atomic:operations": [{
+ "openapi:discriminator": "add-article",
+ "op": "add",
+ "data": {
+ "type": "articles",
+ "attributes": {
+ "openapi:discriminator": "articles",
+ "title": "JSON API paints my bikeshed!"
+ }
+ }
+ }]
+}
+```
+
+## Processing
+
+A server MAY ignore the `openapi:discriminator` member in [attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects from incoming requests.
+A server SHOULD ignore the `openapi:discriminator` member in elements of an `atomic:operations` array.
+
+A server MUST include the `openapi:discriminator` member in [attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects in outgoing responses.
+The member value MUST be the same as the `type` member value of the containing resource object.
+
+A client MAY include the `openapi:discriminator` member in [attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects in outgoing requests.
+The member value MUST be the same as the `type` member value of the containing resource object.
+
+A client MAY include the `openapi:discriminator` member in elements of an `atomic:operations` array.
+
+### Processing Errors
+
+A server SHOULD validate that the value of the `openapi:discriminator` member in
+[attributes](https://jsonapi.org/format/#document-resource-object-attributes) and [relationships](https://jsonapi.org/format/#document-resource-object-relationships) objects
+is identical to the `type` member in the containing resource object. When validation fails, the server MUST respond with a `409 Conflict`
+and SHOULD include a document with a top-level `errors` member that contains an error object.
diff --git a/docs/getting-started/faq.md b/docs/getting-started/faq.md
index 57f1258c24..54b4e50d52 100644
--- a/docs/getting-started/faq.md
+++ b/docs/getting-started/faq.md
@@ -4,10 +4,11 @@
While the [documentation](~/usage/resources/index.md) covers basic features and a few runnable example projects are available [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples),
many more advanced use cases are available as integration tests [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/test/JsonApiDotNetCoreTests/IntegrationTests), so be sure to check them out!
-#### Why can't I use OpenAPI?
-Due to the mismatch between the JSON:API structure and the shape of ASP.NET controller methods, this does not work out of the box.
-This is high on our agenda and we're steadily making progress, but it's quite complex and far from complete.
-See [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/1046) for the current status, which includes instructions on trying out the latest build.
+#### Why don't you use the built-in OpenAPI support in ASP.NET Core?
+The structure of JSON:API request and response bodies differs significantly from the signature of JsonApiDotNetCore controllers.
+JsonApiDotNetCore provides OpenAPI support using [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore), a mature and feature-rich library that is highly extensible.
+The [OpenAPI support in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview) is still very young
+and doesn't provide the level of extensibility needed for JsonApiDotNetCore.
#### What's available to implement a JSON:API client?
It depends on the programming language used. There's an overwhelming list of client libraries at https://jsonapi.org/implementations/#client-libraries.
@@ -71,7 +72,7 @@ For example, if your primary key column is named "CustomerId" instead of "Id":
builder.Entity().Property(x => x.Id).HasColumnName("CustomerId");
```
-It certainly pays off to read up on these capabilities at [Creating and Configuring a Model](https://learn.microsoft.com/en-us/ef/core/modeling/).
+It certainly pays off to read up on these capabilities at [Creating and Configuring a Model](https://learn.microsoft.com/ef/core/modeling/).
Another great resource is [Learn Entity Framework Core](https://www.learnentityframeworkcore.com/configuration).
#### Can I share my resource models with .NET Framework projects?
@@ -80,7 +81,7 @@ This package contains just the JSON:API attributes and targets NetStandard 1.0,
At startup, use [Auto-discovery](~/usage/resource-graph.md#auto-discovery) and point it to your shared project.
#### What's the best place to put my custom business/validation logic?
-For basic input validation, use the attributes from [ASP.NET ModelState Validation](https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?source=recommendations&view=aspnetcore-7.0#built-in-attributes) to get the best experience.
+For basic input validation, use the attributes from [ASP.NET ModelState Validation](https://learn.microsoft.com/aspnet/core/mvc/models/validation?source=recommendations&view=aspnetcore-7.0#built-in-attributes) to get the best experience.
JsonApiDotNetCore is aware of them and adjusts behavior accordingly. And it produces the best possible error responses.
For non-trivial business rules that require custom code, the place to be is [Resource Definitions](~/usage/extensibility/resource-definitions.md).
@@ -148,7 +149,7 @@ And most resource definition callbacks are handled.
That's because the built-in resource service translates all JSON:API query aspects of the request into a database-agnostic data structure called `QueryLayer`.
Now the hard part for you becomes reading that data structure and producing data access calls from that.
If your data store provides a LINQ provider, you can probably reuse [QueryableBuilder](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryableBuilder.cs),
-which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/).
+which drives the translation into [System.Linq.Expressions](https://learn.microsoft.com/dotnet/csharp/programming-guide/concepts/expression-trees/).
Note however, that it also produces calls to `.Include("")`, which is an Entity Framework Core-specific extension method, so you'll need to
[prevent that from happening](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Queries/QueryableBuilding/QueryLayerIncludeConverter.cs).
diff --git a/docs/usage/caching.md b/docs/usage/caching.md
index 537ec70e4b..28d6a6a36e 100644
--- a/docs/usage/caching.md
+++ b/docs/usage/caching.md
@@ -2,7 +2,7 @@
_since v4.2_
-GET requests return an [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) HTTP header, which can be used by the client in subsequent requests to save network bandwidth.
+GET requests return an [ETag](https://developer.mozilla.org/docs/Web/HTTP/Headers/ETag) HTTP header, which can be used by the client in subsequent requests to save network bandwidth.
Be aware that the returned ETag represents the entire response body (a 'resource' in HTTP terminology) for a request URL that includes the query string.
This is unrelated to JSON:API resources. Therefore, we do not use ETags for optimistic concurrency.
diff --git a/docs/usage/common-pitfalls.md b/docs/usage/common-pitfalls.md
index f1f3fed3d6..60162cfd37 100644
--- a/docs/usage/common-pitfalls.md
+++ b/docs/usage/common-pitfalls.md
@@ -50,7 +50,7 @@ Did you notice the missing type of the `LoginAccount.Customer` property? We must
This is only one of the issues you'll run into. Just don't go there.
The right way to model this is by having only `Customer` instead of `WebCustomer` and `AdminCustomer`. And then:
-- Hide the `CreditRating` property for web users using [this](https://www.jsonapi.net/usage/extensibility/resource-definitions.html#excluding-fields) approach.
+- Hide the `CreditRating` property for web users using [this](~/usage/extensibility/resource-definitions.md#excluding-fields) approach.
- Block web users from setting the `CreditRating` property from POST/PATCH resource endpoints by either:
- Detecting if the `CreditRating` property has changed, such as done [here](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/test/JsonApiDotNetCoreTests/IntegrationTests/InputValidation/RequestBody/WorkflowDefinition.cs).
- Injecting `ITargetedFields`, throwing an error when it contains the `CreditRating` property.
@@ -61,7 +61,7 @@ This paradigm [doesn't work well](https://github.com/json-api-dotnet/JsonApiDotN
So if your API needs to guard invariants such as "the sum of all orders must never exceed 500 dollars", then you're better off with an RPC-style API instead of the REST paradigm that JSON:API follows.
Adding constructors to resource classes that validate incoming parameters before assigning them to properties does not work.
-Entity Framework Core [supports](https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties) that,
+Entity Framework Core [supports](https://learn.microsoft.com/ef/core/modeling/constructors#binding-to-mapped-properties) that,
but does so via internal implementation details that are inaccessible by JsonApiDotNetCore.
In JsonApiDotNetCore, resources are what DDD calls [anemic models](https://thedomaindrivendesign.io/anemic-model/).
@@ -84,9 +84,14 @@ With stored procedures, you're either going to have a lot of work to do, or you'
Neither sounds very compelling. If stored procedures is what you need, you're better off creating an RPC-style API that doesn't use JsonApiDotNetCore.
#### Do not use `[ApiController]` on JSON:API controllers
-Although recommended by Microsoft for hard-written controllers, the opinionated behavior of [`[ApiController]`](https://learn.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-7.0#apicontroller-attribute) violates the JSON:API specification.
+Although recommended by Microsoft for hard-written controllers, the opinionated behavior of [`[ApiController]`](https://learn.microsoft.com/aspnet/core/web-api/#apicontroller-attribute) violates the JSON:API specification.
Despite JsonApiDotNetCore trying its best to deal with it, the experience won't be as good as leaving it out.
+#### Don't use auto-generated controllers with shared models
+
+When model classes are defined in a separate project, the controllers are generated in that project as well, which is probably not what you want.
+For details, see [here](~/usage/extensibility/controllers.md#auto-generated-controllers).
+
#### Register/override injectable services
Register your JSON:API resource services, resource definitions and repositories with `services.AddResourceService/AddResourceDefinition/AddResourceRepository()` instead of `services.AddScoped()`.
When using [Auto-discovery](~/usage/resource-graph.md#auto-discovery), you don't need to register these at all.
@@ -102,7 +107,7 @@ If you're in need of a quick setup, use [SQLite](https://www.sqlite.org/). After
builder.Services.AddSqlite("Data Source=temp.db");
```
Which creates `temp.db` on disk. Simply deleting the file gives you a clean slate.
-This is a lot more convenient compared to using [SqlLocalDB](https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-express-localdb), which runs a background service that breaks if you delete its underlying storage files.
+This is a lot more convenient compared to using [SqlLocalDB](https://learn.microsoft.com/sql/database-engine/configure-windows/sql-server-express-localdb), which runs a background service that breaks if you delete its underlying storage files.
However, even SQLite does not support all queries produced by Entity Framework Core. You'll get the best (and fastest) experience with [PostgreSQL in a docker container](https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/run-docker-postgres.ps1).
@@ -140,6 +145,6 @@ If you need such side effects, it's easiest to inject your `DbContext` in the co
A better way is to inject your `DbContext` in a [Resource Definition](~/usage/extensibility/resource-definitions.md) and apply the changes there.
#### Concurrency tokens (timestamp/rowversion/xmin) won't work
-While we'd love to support such [tokens for optimistic concurrency](https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=data-annotations),
+While we'd love to support such [tokens for optimistic concurrency](https://learn.microsoft.com/ef/core/saving/concurrency),
it turns out that the implementation is far from trivial. We've come a long way, but aren't sure how it should work when relationship endpoints and atomic operations are involved.
If you're interested, we welcome your feedback at https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1119.
diff --git a/docs/usage/extensibility/controllers.md b/docs/usage/extensibility/controllers.md
index 68e1d86ea3..254b305ed9 100644
--- a/docs/usage/extensibility/controllers.md
+++ b/docs/usage/extensibility/controllers.md
@@ -6,7 +6,7 @@ To expose API endpoints, ASP.NET controllers need to be defined.
_since v5_
-Controllers are auto-generated (using [source generators](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview)) when you add `[Resource]` on your model class:
+Controllers are auto-generated (using [source generators](https://learn.microsoft.com/dotnet/csharp/roslyn-sdk/#source-generators)) when you add `[Resource]` on your model class:
```c#
[Resource] // Generates ArticlesController.g.cs
diff --git a/docs/usage/extensibility/middleware.md b/docs/usage/extensibility/middleware.md
index 62528893d3..dbbe81699f 100644
--- a/docs/usage/extensibility/middleware.md
+++ b/docs/usage/extensibility/middleware.md
@@ -3,9 +3,9 @@
The default middleware validates incoming `Content-Type` and `Accept` HTTP headers.
Based on routing configuration, it fills `IJsonApiRequest`, an injectable object that contains JSON:API-related information about the request being processed.
-It is possible to replace the built-in middleware components by configuring the IoC container and by configuring `MvcOptions`.
+It is possible to replace the built-in middleware components by configuring the IoC container and by configuring `MvcOptions`.
-## Configuring the IoC container
+## Configuring the IoC container
The following example replaces the internal exception filter with a custom implementation.
diff --git a/docs/usage/extensibility/resource-definitions.md b/docs/usage/extensibility/resource-definitions.md
index cf5400b722..644d43fb75 100644
--- a/docs/usage/extensibility/resource-definitions.md
+++ b/docs/usage/extensibility/resource-definitions.md
@@ -29,7 +29,7 @@ For various reasons (see examples below) you may need to change parts of the que
`JsonApiResourceDefinition` (which is an empty implementation of `IResourceDefinition`) provides overridable methods that pass you the result of query string parameter parsing.
The value returned by you determines what will be used to execute the query.
-An intermediate format (`QueryExpression` and derived types) is used, which enables us to separate JSON:API implementation
+An intermediate format (`QueryExpression` and derived types) is used, which enables us to separate JSON:API implementation
from Entity Framework Core `IQueryable` execution.
### Excluding fields
@@ -220,7 +220,7 @@ You can define additional query string parameters with the LINQ expression that
If the key is present in a query string, the supplied LINQ expression will be added to the database query.
> [!NOTE]
-> This directly influences the Entity Framework Core `IQueryable`. As opposed to using `OnApplyFilter`, this enables the full range of Entity Framework Core operators.
+> This directly influences the Entity Framework Core `IQueryable`. As opposed to using `OnApplyFilter`, this enables the full range of Entity Framework Core operators.
But it only works on primary resource endpoints (for example: /articles, but not on /blogs/1/articles or /blogs?include=articles).
```c#
diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md
new file mode 100644
index 0000000000..c0717d47ca
--- /dev/null
+++ b/docs/usage/openapi-client.md
@@ -0,0 +1,356 @@
+> [!WARNING]
+> OpenAPI support for JSON:API is currently experimental. The API and the structure of the OpenAPI document may change in future versions.
+
+# OpenAPI clients
+
+After [enabling OpenAPI](~/usage/openapi.md), you can generate a typed JSON:API client for your API in various programming languages.
+
+> [!NOTE]
+> If you prefer a generic JSON:API client instead of a typed one, choose from the existing
+> [client libraries](https://jsonapi.org/implementations/#client-libraries).
+
+The following code generators are supported, though you may try others as well:
+- [NSwag](https://github.com/RicoSuter/NSwag) (v14.1 or higher): Produces clients for C# (requires `Newtonsoft.Json`) and TypeScript
+- [Kiota](https://learn.microsoft.com/openapi/kiota/overview): Produces clients for C#, Go, Java, PHP, Python, Ruby, Swift and TypeScript
+
+# [NSwag](#tab/nswag)
+
+For C# clients, we provide an additional package that provides workarounds for bugs in NSwag and enables using partial POST/PATCH requests.
+
+To add it to your project, run the following command:
+```
+dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag
+```
+
+# [Kiota](#tab/kiota)
+
+For C# clients, we provide an additional package that provides workarounds for bugs in Kiota, as well as MSBuild integration.
+
+To add it to your project, run the following command:
+```
+dotnet add package JsonApiDotNetCore.OpenApi.Client.Kiota
+```
+
+---
+
+## Getting started
+
+To generate your C# client, follow the steps below.
+
+# [NSwag](#tab/nswag)
+
+### Visual Studio
+
+The easiest way to get started is by using the built-in capabilities of Visual Studio.
+The following steps describe how to generate and use a JSON:API client in C#, combined with our NuGet package.
+
+1. In **Solution Explorer**, right-click your client project, select **Add** > **Service Reference** and choose **OpenAPI**.
+
+1. On the next page, specify the OpenAPI URL to your JSON:API server, for example: `http://localhost:14140/swagger/v1/swagger.json`.
+ Specify `ExampleApiClient` as the class name, optionally provide a namespace and click **Finish**.
+ Visual Studio now downloads your swagger.json and updates your project file.
+ This adds a pre-build step that generates the client code.
+
+ > [!TIP]
+ > To later re-download swagger.json and regenerate the client code,
+ > right-click **Dependencies** > **Manage Connected Services** and click the **Refresh** icon.
+
+1. Run package update now, which fixes incompatibilities and bugs from older versions.
+
+1. Add our client package to your project:
+
+ ```
+ dotnet add package JsonApiDotNetCore.OpenApi.Client.NSwag
+ ```
+
+1. Add code that calls one of your JSON:API endpoints.
+
+ ```c#
+ using var httpClient = new HttpClient();
+ var apiClient = new ExampleApiClient(httpClient);
+
+ var getResponse = await apiClient.GetPersonCollectionAsync(new Dictionary
+ {
+ ["filter"] = "has(assignedTodoItems)",
+ ["sort"] = "-lastName",
+ ["page[size]"] = "5"
+ });
+
+ foreach (var person in getResponse.Data)
+ {
+ Console.WriteLine($"Found person {person.Id}: {person.Attributes!.DisplayName}");
+ }
+ ```
+
+1. Extend the demo code to send a partial PATCH request with the help of our package:
+
+ ```c#
+ var updatePersonRequest = new UpdatePersonRequestDocument
+ {
+ Data = new DataInUpdatePersonRequest
+ {
+ Id = "1",
+ // Using TrackChangesFor to send "firstName: null" instead of omitting it.
+ Attributes = new TrackChangesFor(_apiClient)
+ {
+ Initializer =
+ {
+ FirstName = null,
+ LastName = "Doe"
+ }
+ }.Initializer
+ }
+ };
+
+ await ApiResponse.TranslateAsync(async () =>
+ await _apiClient.PatchPersonAsync(updatePersonRequest.Data.Id, updatePersonRequest));
+
+ // The sent request looks like this:
+ // {
+ // "data": {
+ // "type": "people",
+ // "id": "1",
+ // "attributes": {
+ // "firstName": null,
+ // "lastName": "Doe"
+ // }
+ // }
+ // }
+ ```
+
+> [!TIP]
+> The [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiNSwagClientExample) contains an enhanced version
+> that uses `IHttpClientFactory` for [scalability](https://learn.microsoft.com/dotnet/core/extensions/httpclient-factory) and
+> [resiliency](https://learn.microsoft.com/aspnet/core/fundamentals/http-requests#use-polly-based-handlers) and logs the HTTP requests and responses.
+> Additionally, the example shows how to write the swagger.json file to disk when building the server, which is imported from the client project.
+> This keeps the server and client automatically in sync, which is handy when both are in the same solution.
+
+### Other IDEs
+
+The following section shows what to add to your client project file directly:
+
+```xml
+
+
+
+
+
+
+
+
+ http://localhost:14140/swagger/v1/swagger.json
+ ExampleApiClient
+ %(ClassName).cs
+
+
+```
+
+From here, continue from step 3 in the list of steps for Visual Studio.
+
+# [Kiota](#tab/kiota)
+
+To generate your C# client, first add the Kiota tool to your solution:
+
+```
+dotnet tool install microsoft.openapi.kiota
+```
+
+After adding the `JsonApiDotNetCore.OpenApi.Client.Kiota` package to your project, add a `KiotaReference` element
+to your project file to import your OpenAPI file. For example:
+
+```xml
+
+
+
+ $(MSBuildProjectName).GeneratedCode
+ ExampleApiClient
+ ./GeneratedCode
+ $(JsonApiExtraArguments)
+
+
+
+```
+
+> [!NOTE]
+> The `ExtraArguments` parameter is required for compatibility with JSON:API.
+
+Next, build your project. It runs the kiota command-line tool, which generates files in the `GeneratedCode` subdirectory.
+
+> [!CAUTION]
+> If you're not using ``, at least make sure you're passing the `--backing-store` switch to the command-line tool,
+> which is needed for JSON:API partial POST/PATCH requests to work correctly.
+
+Kiota is pretty young and therefore still rough around the edges. At the time of writing, there are various bugs, for which we have workarounds
+in place. For a full example, see the [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiKiotaClientExample).
+
+---
+
+## Configuration
+
+Various switches enable you to tweak the client generation to your needs. See the section below for an overview.
+
+# [NSwag](#tab/nswag)
+
+The `OpenApiReference` element can be customized using various [NSwag-specific MSBuild properties](https://github.com/RicoSuter/NSwag/blob/7d6df3af95081f3f0ed6dee04be8d27faa86f91a/src/NSwag.ApiDescription.Client/NSwag.ApiDescription.Client.props).
+See [the source code](https://github.com/RicoSuter/NSwag/blob/master/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs) for their meaning.
+The `JsonApiDotNetCore.OpenApi.Client.NSwag` package sets various of these for optimal JSON:API support.
+
+> [!NOTE]
+> Earlier versions of NSwag required the use of `` to specify command-line switches directly.
+> This is no longer recommended and may conflict with the new MSBuild properties.
+
+For example, the following section puts the generated code in a namespace, makes the client class internal and generates an interface (handy when writing tests):
+
+```xml
+
+ ExampleProject.GeneratedCode
+ internal
+ true
+
+```
+
+# [Kiota](#tab/kiota)
+
+The available command-line switches for Kiota are described [here](https://learn.microsoft.com/openapi/kiota/using#client-generation).
+
+At the time of writing, Kiota provides [no official integration](https://github.com/microsoft/kiota/issues/3005) with MSBuild.
+Our [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiKiotaClientExample) takes a stab at it,
+which seems to work. If you're an MSBuild expert, please help out!
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+---
+
+## Headers and caching
+
+The use of HTTP headers varies per client generator. To use [ETags for caching](~/usage/caching.md), see the notes below.
+
+# [NSwag](#tab/nswag)
+
+To gain access to HTTP response headers, add the following in a `PropertyGroup` or directly in the `OpenApiReference`:
+
+```
+true
+```
+
+This enables the following code, which is explained below:
+
+```c#
+var getResponse = await ApiResponse.TranslateAsync(() => apiClient.GetPersonCollectionAsync());
+string eTag = getResponse.Headers["ETag"].Single();
+Console.WriteLine($"Retrieved {getResponse.Result?.Data.Count ?? 0} people.");
+
+// wait some time...
+
+getResponse = await ApiResponse.TranslateAsync(() => apiClient.GetPersonCollectionAsync(if_None_Match: eTag));
+
+if (getResponse is { StatusCode: (int)HttpStatusCode.NotModified, Result: null })
+{
+ Console.WriteLine("The HTTP response hasn't changed, so no response body was returned.");
+}
+```
+
+The response of the first API call contains both data and an ETag header, which is a fingerprint of the response.
+That ETag gets passed to the second API call. This enables the server to detect if something changed, which optimizes
+network usage: no data is sent back, unless is has changed.
+If you only want to ask whether data has changed without fetching it, use a HEAD request instead.
+
+# [Kiota](#tab/kiota)
+
+Use `HeadersInspectionHandlerOption` to gain access to HTTP response headers. For example:
+
+```c#
+var headerInspector = new HeadersInspectionHandlerOption
+{
+ InspectResponseHeaders = true
+};
+
+var responseDocument = await apiClient.Api.People.GetAsync(configuration => configuration.Options.Add(headerInspector));
+
+string eTag = headerInspector.ResponseHeaders["ETag"].Single();
+```
+
+Due to a [bug in Kiota](https://github.com/microsoft/kiota/issues/4190), a try/catch block is needed additionally to make this work.
+
+For a full example, see the [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiKiotaClientExample).
+
+---
+
+## Atomic operations
+
+# [NSwag](#tab/nswag)
+
+[Atomic operations](~/usage/writing/bulk-batch-operations.md) are fully supported.
+The [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiNSwagClientExample)
+demonstrates how to use them. It uses local IDs to:
+- Create a new tag
+- Create a new person
+- Update the person to clear an attribute (using `TrackChangesFor`)
+- Create a new todo-item, tagged with the new tag, and owned by the new person
+- Assign the todo-item to the created person
+
+# [Kiota](#tab/kiota)
+
+[Atomic operations](~/usage/writing/bulk-batch-operations.md) are fully supported.
+See the [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples/OpenApiKiotaClientExample)
+demonstrates how to use them. It uses local IDs to:
+- Create a new tag
+- Create a new person
+- Update the person to clear an attribute (using built-in backing-store)
+- Create a new todo-item, tagged with the new tag, and owned by the new person
+- Assign the todo-item to the created person
+
+---
+
+## Known limitations
+
+# [NSwag](#tab/nswag)
+
+| Limitation | Workaround | Links |
+| --- | --- | --- |
+| Partial POST/PATCH sends incorrect request | Use `TrackChangesFor` from `JsonApiDotNetCore.OpenApi.Client.NSwag` package | |
+| Exception thrown on successful HTTP status | Use `TranslateAsync` from `JsonApiDotNetCore.OpenApi.Client.NSwag` package | https://github.com/RicoSuter/NSwag/issues/2499 |
+| No `Accept` header sent when only error responses define `Content-Type` | `JsonApiDotNetCore.OpenApi.Swashbuckle` package contains workaround | |
+| Schema type not always inferred with `allOf` | `JsonApiDotNetCore.OpenApi.Swashbuckle` package contains workaround | |
+| Generated code for JSON:API extensions does not compile | `JsonApiDotNetCore.OpenApi.Swashbuckle` package contains workaround | |
+| A project can't contain both JSON:API clients and regular OpenAPI clients | Use separate projects | |
+
+# [Kiota](#tab/kiota)
+
+| Limitation | Workaround | Links |
+| --- | --- | --- |
+| Properties are always nullable | - | https://github.com/microsoft/kiota/issues/3911 |
+| JSON:API query strings are inaccessible | Use `SetQueryStringHttpMessageHandler.CreateScope` from `JsonApiDotNetCore.OpenApi.Client.Kiota` package | https://github.com/microsoft/kiota/issues/3800 |
+| Properties set to `null` are sent twice | - | https://github.com/microsoft/kiota-dotnet/issues/535 |
+| HTTP 304 (Not Modified) is not properly recognized | Catch `ApiException` and inspect the response status code | https://github.com/microsoft/kiota/issues/4190, https://github.com/microsoft/kiota-dotnet/issues/531 |
+| Generator warns about unsupported formats | Use `JsonApiDotNetCore.OpenApi.Client.Kiota` package | https://github.com/microsoft/kiota/issues/4227 |
+| `Stream` response for HEAD request | - | https://github.com/microsoft/kiota/issues/4245 |
+| Unhelpful exception messages | - | https://github.com/microsoft/kiota/issues/4349 |
+| Discriminator properties aren't being set automatically | - | https://github.com/microsoft/kiota/issues/4618 |
+| Discriminator mappings must be repeated in every derived type used in responses | `JsonApiDotNetCore.OpenApi.Swashbuckle` package contains workaround | https://github.com/microsoft/kiota/issues/2432 |
+| `x-abstract` in `openapi.json` is ignored | - | |
+| No MSBuild / IDE support | Use `KiotaReference` from `JsonApiDotNetCore.OpenApi.Client.Kiota` package | https://github.com/microsoft/kiota/issues/3005 |
+| Incorrect nullability in API methods | Use `KiotaReference` from `JsonApiDotNetCore.OpenApi.Client.Kiota` package | https://github.com/microsoft/kiota/issues/3944 |
+| Generated code for JSON:API extensions does not compile | `JsonApiDotNetCore.OpenApi.Swashbuckle` package contains workaround | |
+| Properties are always sent in alphabetic order | - | https://github.com/microsoft/kiota/issues/4680 |
+
+---
diff --git a/docs/usage/openapi-documentation.md b/docs/usage/openapi-documentation.md
new file mode 100644
index 0000000000..6737bd6404
--- /dev/null
+++ b/docs/usage/openapi-documentation.md
@@ -0,0 +1,48 @@
+> [!WARNING]
+> OpenAPI support for JSON:API is currently experimental. The API and the structure of the OpenAPI document may change in future versions.
+
+# OpenAPI documentation
+
+After [enabling OpenAPI](~/usage/openapi.md), you can expose a documentation website with SwaggerUI, Redoc and/or Scalar.
+
+## SwaggerUI
+
+[SwaggerUI](https://swagger.io/tools/swagger-ui/) enables to visualize and interact with the JSON:API endpoints through a web page.
+While it conveniently provides the ability to execute requests, it doesn't show properties of derived types when component schema inheritance is used.
+
+SwaggerUI can be enabled by installing the `Swashbuckle.AspNetCore.SwaggerUI` NuGet package and adding the following to your `Program.cs` file:
+
+```c#
+app.UseSwaggerUI();
+```
+
+Then run your app and open `/swagger` in your browser.
+
+## Redoc
+
+[Redoc](https://github.com/Redocly/redoc) is another popular tool that generates a documentation website from an OpenAPI document.
+It lists the endpoints and their schemas, but doesn't provide the ability to execute requests.
+However, this tool most accurately reflects properties when component schema inheritance is used; choosing a different "type" from the
+dropdown box dynamically adapts the list of schema properties.
+
+The `Swashbuckle.AspNetCore.ReDoc` NuGet package provides integration with Swashbuckle.
+After installing the package, add the following to your `Program.cs` file:
+
+```c#
+app.UseReDoc();
+```
+
+Next, run your app and navigate to `/api-docs` to view the documentation.
+
+## Scalar
+
+[Scalar](https://scalar.com/) is a modern documentation website generator, which includes the ability to execute requests.
+It shows component schemas in a low-level way (not collapsing `allOf` nodes), but does a poor job in handling component schema inheritance.
+
+After installing the `Scalar.AspNetCore` NuGet package, add the following to your `Program.cs` to make it use the OpenAPI document produced by Swashbuckle:
+
+```c#
+app.MapScalarApiReference(options => options.OpenApiRoutePattern = "/swagger/{documentName}/swagger.json");
+```
+
+Then run your app and navigate to `/scalar/v1` to view the documentation.
diff --git a/docs/usage/openapi.md b/docs/usage/openapi.md
new file mode 100644
index 0000000000..e49f120a14
--- /dev/null
+++ b/docs/usage/openapi.md
@@ -0,0 +1,87 @@
+> [!WARNING]
+> OpenAPI support for JSON:API is currently experimental. The API and the structure of the OpenAPI document may change in future versions.
+
+# OpenAPI
+
+Exposing an [OpenAPI document](https://swagger.io/specification/) for your JSON:API endpoints enables to provide a
+[documentation website](https://swagger.io/tools/swagger-ui/) and to generate typed
+[client libraries](https://openapi-generator.tech/docs/generators/) in various languages.
+
+The [JsonApiDotNetCore.OpenApi.Swashbuckle](https://github.com/json-api-dotnet/JsonApiDotNetCore/pkgs/nuget/JsonApiDotNetCore.OpenApi.Swashbuckle) NuGet package
+provides OpenAPI support for JSON:API by integrating with [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore).
+
+## Getting started
+
+1. Install the `JsonApiDotNetCore.OpenApi.Swashbuckle` NuGet package:
+
+ ```
+ dotnet add package JsonApiDotNetCore.OpenApi.Swashbuckle
+ ```
+
+ > [!NOTE]
+ > Because this package is still experimental, it's not yet available on NuGet.
+ > Use the steps [here](https://github.com/json-api-dotnet/JsonApiDotNetCore?tab=readme-ov-file#trying-out-the-latest-build) to install.
+
+2. Add the JSON:API support to your `Program.cs` file.
+
+ ```c#
+ builder.Services.AddJsonApi();
+
+ // Configure Swashbuckle for JSON:API.
+ builder.Services.AddOpenApiForJsonApi();
+
+ var app = builder.Build();
+
+ app.UseRouting();
+ app.UseJsonApi();
+
+ // Add the Swashbuckle middleware.
+ app.UseSwagger();
+ ```
+
+By default, the OpenAPI document will be available at `http://localhost:/swagger/v1/swagger.json`.
+
+### Customizing the Route Template
+
+Because Swashbuckle doesn't properly implement the ASP.NET Options pattern, you must *not* use its
+[documented way](https://github.com/domaindrivendev/Swashbuckle.AspNetCore?tab=readme-ov-file#change-the-path-for-swagger-json-endpoints)
+to change the route template:
+
+```c#
+// DO NOT USE THIS! INCOMPATIBLE WITH JSON:API!
+app.UseSwagger(options => options.RouteTemplate = "api-docs/{documentName}/swagger.yaml");
+```
+
+Instead, always call `UseSwagger()` *without parameters*. To change the route template, use the code below:
+
+```c#
+builder.Services.Configure(options => options.RouteTemplate = "/api-docs/{documentName}/swagger.yaml");
+```
+
+If you want to inject dependencies to set the route template, use:
+
+```c#
+builder.Services.AddOptions().Configure((options, serviceProvider) =>
+{
+ var webHostEnvironment = serviceProvider.GetRequiredService();
+ string appName = webHostEnvironment.ApplicationName;
+ options.RouteTemplate = $"/api-docs/{{documentName}}/{appName}-swagger.yaml";
+});
+```
+
+## Triple-slash comments
+
+Documentation for JSON:API endpoints is provided out of the box, which shows in SwaggerUI and through IDE IntelliSense in auto-generated clients.
+To also get documentation for your resource classes and their properties, add the following to your project file.
+The `NoWarn` line is optional, which suppresses build warnings for undocumented types and members.
+
+```xml
+
+ True
+ $(NoWarn);1591
+
+```
+
+You can combine this with the documentation that Swagger itself supports, by enabling it as described
+[here](https://github.com/domaindrivendev/Swashbuckle.AspNetCore#include-descriptions-from-xml-comments).
+This adds documentation for additional types, such as triple-slash comments on enums used in your resource models.
diff --git a/docs/usage/options.md b/docs/usage/options.md
index 7607ac8a9e..7e89ff0090 100644
--- a/docs/usage/options.md
+++ b/docs/usage/options.md
@@ -122,7 +122,7 @@ Because we copy resource properties into an intermediate object before serializa
## ModelState Validation
-[ASP.NET ModelState validation](https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation) can be used to validate incoming request bodies when creating and updating resources. Since v5.0, this is enabled by default.
+[ASP.NET ModelState validation](https://learn.microsoft.com/aspnet/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api) can be used to validate incoming request bodies when creating and updating resources. Since v5.0, this is enabled by default.
When `ValidateModelState` is set to `false`, no model validation is performed.
How nullability affects ModelState validation is described [here](~/usage/resources/nullability.md).
diff --git a/docs/usage/reading/filtering.md b/docs/usage/reading/filtering.md
index 8a568078a0..05c3066644 100644
--- a/docs/usage/reading/filtering.md
+++ b/docs/usage/reading/filtering.md
@@ -60,7 +60,7 @@ The next request returns all customers that have orders -or- whose last name is
GET /customers?filter=has(orders)&filter=equals(lastName,'Smith') HTTP/1.1
```
-Aside from filtering on the resource being requested (which would be blogs in /blogs and articles in /blogs/1/articles),
+Aside from filtering on the resource being requested (which would be blogs in /blogs and articles in /blogs/1/articles),
filtering on to-many relationships can be done using bracket notation:
```http
diff --git a/docs/usage/resource-graph.md b/docs/usage/resource-graph.md
index 18a13da907..046daaf7f5 100644
--- a/docs/usage/resource-graph.md
+++ b/docs/usage/resource-graph.md
@@ -14,7 +14,7 @@ There are three ways the resource graph can be created:
2. Specifying an entire DbContext
3. Manually specifying each resource
-It is also possible to combine the three of them at once. Be aware that some configuration might overlap,
+It is also possible to combine the three of them at once. Be aware that some configuration might overlap,
for example one could manually add a resource to the graph which is also auto-discovered. In such a scenario, the configuration
is prioritized by the list above in descending order.
diff --git a/docs/usage/resources/nullability.md b/docs/usage/resources/nullability.md
index 24b15572fc..875b133a01 100644
--- a/docs/usage/resources/nullability.md
+++ b/docs/usage/resources/nullability.md
@@ -24,7 +24,7 @@ This makes Entity Framework Core generate non-nullable columns. And model errors
# Reference types
-When the [nullable reference types](https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references) (NRT) compiler feature is enabled, it affects both ASP.NET ModelState validation and Entity Framework Core.
+When the [nullable reference types](https://learn.microsoft.com/dotnet/csharp/nullable-references) (NRT) compiler feature is enabled, it affects both ASP.NET ModelState validation and Entity Framework Core.
## NRT turned off
@@ -60,7 +60,7 @@ public sealed class Label : Identifiable
When NRT is turned on, use nullability annotations (?) on attributes and relationships. This makes Entity Framework Core generate non-nullable columns. And model errors are returned when non-nullable fields are omitted.
-The [Entity Framework Core guide on NRT](https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types) recommends to use constructor binding to initialize non-nullable properties, but JsonApiDotNetCore does not support that. For required navigation properties, it suggests to use a non-nullable property with a nullable backing field. JsonApiDotNetCore does not support that either. In both cases, just use the null-forgiving operator (!).
+The [Entity Framework Core guide on NRT](https://learn.microsoft.com/ef/core/miscellaneous/nullable-reference-types) recommends to use constructor binding to initialize non-nullable properties, but JsonApiDotNetCore does not support that. For required navigation properties, it suggests to use a non-nullable property with a nullable backing field. JsonApiDotNetCore does not support that either. In both cases, just use the null-forgiving operator (!).
When ModelState validation is turned on, to-many relationships must be assigned an empty collection. Otherwise an error is returned when they don't occur in the request body.
diff --git a/docs/usage/resources/relationships.md b/docs/usage/resources/relationships.md
index 689b3aa4d2..f318b2ddcd 100644
--- a/docs/usage/resources/relationships.md
+++ b/docs/usage/resources/relationships.md
@@ -1,7 +1,7 @@
# Relationships
A relationship is a named link between two resource types, including a direction.
-They are similar to [navigation properties in Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/modeling/relationships).
+They are similar to [navigation properties in Entity Framework Core](https://learn.microsoft.com/ef/core/modeling/relationships).
Relationships come in two flavors: to-one and to-many.
The left side of a relationship is where the relationship is declared, the right side is the resource type it points to.
@@ -113,7 +113,7 @@ For optional one-to-one relationships, Entity Framework Core uses `DeleteBehavio
This means that Entity Framework Core tries to handle the cascading effects (by sending multiple SQL statements), instead of leaving it up to the database.
Of course that's only going to work when all the related resources are loaded in the change tracker upfront, which is expensive because it requires fetching more data than necessary.
-The reason for this odd default is poor support in SQL Server, as explained [here](https://stackoverflow.com/questions/54326165/ef-core-why-clientsetnull-is-default-ondelete-behavior-for-optional-relations) and [here](https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete#database-cascade-limitations).
+The reason for this odd default is poor support in SQL Server, as explained [here](https://stackoverflow.com/questions/54326165/ef-core-why-clientsetnull-is-default-ondelete-behavior-for-optional-relations) and [here](https://learn.microsoft.com/ef/core/saving/cascade-delete#database-cascade-limitations).
**Our [testing](https://github.com/json-api-dotnet/JsonApiDotNetCore/pull/1205) shows that these limitations don't exist when using PostgreSQL.
Therefore the general advice is to map the delete behavior of optional one-to-one relationships explicitly with `.OnDelete(DeleteBehavior.SetNull)`. This is simpler and more efficient.**
@@ -261,8 +261,8 @@ public class TodoItem : Identifiable
_since v5.1_
-Default JSON:API relationship capabilities are specified in
-@JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_DefaultHasOneCapabilities and
+Default JSON:API relationship capabilities are specified in
+@JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_DefaultHasOneCapabilities and
@JsonApiDotNetCore.Configuration.JsonApiOptions#JsonApiDotNetCore_Configuration_JsonApiOptions_DefaultHasManyCapabilities:
```c#
diff --git a/docs/usage/routing.md b/docs/usage/routing.md
index e3e021ec23..cb1197e86a 100644
--- a/docs/usage/routing.md
+++ b/docs/usage/routing.md
@@ -86,7 +86,7 @@ public class OrderLineController : JsonApiController
## Advanced usage: custom routing convention
-It is possible to replace the built-in routing convention with a [custom routing convention](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/application-model?view=aspnetcore-3.1#sample-custom-routing-convention) by registering an implementation of `IJsonApiRoutingConvention`.
+It is possible to replace the built-in routing convention with a [custom routing convention](https://learn.microsoft.com/aspnet/core/mvc/controllers/application-model#custom-routing-convention) by registering an implementation of `IJsonApiRoutingConvention`.
```c#
// Program.cs
diff --git a/docs/usage/toc.md b/docs/usage/toc.md
index 61d3da8de0..bdeb0e4958 100644
--- a/docs/usage/toc.md
+++ b/docs/usage/toc.md
@@ -25,6 +25,10 @@
# [Caching](caching.md)
# [Common Pitfalls](common-pitfalls.md)
+# [OpenAPI](openapi.md)
+## [Documentation](openapi-documentation.md)
+## [Clients](openapi-client.md)
+
# Extensibility
## [Layer Overview](extensibility/layer-overview.md)
## [Resource Definitions](extensibility/resource-definitions.md)
diff --git a/docs/usage/writing/bulk-batch-operations.md b/docs/usage/writing/bulk-batch-operations.md
index 5756755b51..c8ba2bf48e 100644
--- a/docs/usage/writing/bulk-batch-operations.md
+++ b/docs/usage/writing/bulk-batch-operations.md
@@ -28,7 +28,7 @@ public sealed class OperationsController : JsonApiOperationsController
}
```
-> [!IMPORTANT]
+> [!IMPORTANT]
> Since v5.6.0, the set of exposed operations is based on
> [`GenerateControllerEndpoints` usage](~/usage/extensibility/controllers.md#resource-access-control).
> Earlier versions always exposed all operations for all resource types.
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000000..16164967c6
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/package-versions.props b/package-versions.props
index 9ba0b34bbb..8b528ff324 100644
--- a/package-versions.props
+++ b/package-versions.props
@@ -4,39 +4,49 @@
4.1.00.4.12.14.1
+ 8.0.0
+ 13.0.3
- 0.13.*
- 35.5.*
- 4.10.*
+ 0.14.*
+ 1.0.*
+ 35.6.*
+ 4.13.*6.0.*2.1.*
- 6.12.*
- 2.3.*
+ 7.2.*
+ 2.4.*2.0.*
- 8.0.*
- 17.10.*
- 2.8.*
+ 1.*
+ 9.0.*
+ 9.0.*
+ 14.3.*
+ 13.0.*
+ 2.1.*
+ 8.*-*
+ 9.0.*
+ 17.13.*
+ 2.9.*
+ 2.8.*
-
+
- 8.0.0
+ N/A
- 8.0.*
- 8.0.*
- $(AspNetCoreVersion)
+ 9.0.*
+ 9.0.*
+ 9.0.0-*
-
+
- 6.0.0
+ 8.0.0
- 6.0.*
- 2.1.*
- 7.0.*
- 7.0.*
+ 8.0.*
+ 8.0.*
+ $(EntityFrameworkCoreVersion)
diff --git a/src/Examples/DapperExample/AtomicOperations/AmbientTransaction.cs b/src/Examples/DapperExample/AtomicOperations/AmbientTransaction.cs
index c442861bc4..11d052da66 100644
--- a/src/Examples/DapperExample/AtomicOperations/AmbientTransaction.cs
+++ b/src/Examples/DapperExample/AtomicOperations/AmbientTransaction.cs
@@ -1,5 +1,4 @@
using System.Data.Common;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.AtomicOperations;
namespace DapperExample.AtomicOperations;
@@ -18,8 +17,8 @@ internal sealed class AmbientTransaction : IOperationsTransaction
public AmbientTransaction(AmbientTransactionFactory owner, DbTransaction current, Guid transactionId)
{
- ArgumentGuard.NotNull(owner);
- ArgumentGuard.NotNull(current);
+ ArgumentNullException.ThrowIfNull(owner);
+ ArgumentNullException.ThrowIfNull(current);
_owner = owner;
Current = current;
diff --git a/src/Examples/DapperExample/AtomicOperations/AmbientTransactionFactory.cs b/src/Examples/DapperExample/AtomicOperations/AmbientTransactionFactory.cs
index d10959b79a..82790819fe 100644
--- a/src/Examples/DapperExample/AtomicOperations/AmbientTransactionFactory.cs
+++ b/src/Examples/DapperExample/AtomicOperations/AmbientTransactionFactory.cs
@@ -1,6 +1,5 @@
using System.Data.Common;
using DapperExample.TranslationToSql.DataModel;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.AtomicOperations;
using JsonApiDotNetCore.Configuration;
@@ -18,8 +17,8 @@ public sealed class AmbientTransactionFactory : IOperationsTransactionFactory
public AmbientTransactionFactory(IJsonApiOptions options, IDataModelService dataModelService)
{
- ArgumentGuard.NotNull(options);
- ArgumentGuard.NotNull(dataModelService);
+ ArgumentNullException.ThrowIfNull(options);
+ ArgumentNullException.ThrowIfNull(dataModelService);
_options = options;
_dataModelService = dataModelService;
@@ -64,7 +63,7 @@ async Task IOperationsTransactionFactory.BeginTransactio
internal void Detach(AmbientTransaction ambientTransaction)
{
- ArgumentGuard.NotNull(ambientTransaction);
+ ArgumentNullException.ThrowIfNull(ambientTransaction);
if (AmbientTransaction != null && AmbientTransaction == ambientTransaction)
{
diff --git a/src/Examples/DapperExample/Controllers/OperationsController.cs b/src/Examples/DapperExample/Controllers/OperationsController.cs
index 2b9daf492f..ed15c6e9a2 100644
--- a/src/Examples/DapperExample/Controllers/OperationsController.cs
+++ b/src/Examples/DapperExample/Controllers/OperationsController.cs
@@ -8,5 +8,5 @@ namespace DapperExample.Controllers;
public sealed class OperationsController(
IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request,
- ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor,
- request, targetedFields, operationFilter);
+ ITargetedFields targetedFields, IAtomicOperationFilter operationFilter)
+ : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter);
diff --git a/src/Examples/DapperExample/DapperExample.csproj b/src/Examples/DapperExample/DapperExample.csproj
index f49c3e4b40..ed7bd358eb 100644
--- a/src/Examples/DapperExample/DapperExample.csproj
+++ b/src/Examples/DapperExample/DapperExample.csproj
@@ -1,6 +1,6 @@
- net8.0;net6.0
+ net9.0;net8.0
@@ -16,6 +16,6 @@
-
+
diff --git a/src/Examples/DapperExample/Data/AppDbContext.cs b/src/Examples/DapperExample/Data/AppDbContext.cs
index ee18bab08e..31f09b277c 100644
--- a/src/Examples/DapperExample/Data/AppDbContext.cs
+++ b/src/Examples/DapperExample/Data/AppDbContext.cs
@@ -1,6 +1,5 @@
using DapperExample.Models;
using JetBrains.Annotations;
-using JsonApiDotNetCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -23,7 +22,7 @@ public sealed class AppDbContext : DbContext
public AppDbContext(DbContextOptions options, IConfiguration configuration)
: base(options)
{
- ArgumentGuard.NotNull(configuration);
+ ArgumentNullException.ThrowIfNull(configuration);
_configuration = configuration;
}
diff --git a/src/Examples/DapperExample/Definitions/TodoItemDefinition.cs b/src/Examples/DapperExample/Definitions/TodoItemDefinition.cs
index dc7c1802c8..f20268fb7d 100644
--- a/src/Examples/DapperExample/Definitions/TodoItemDefinition.cs
+++ b/src/Examples/DapperExample/Definitions/TodoItemDefinition.cs
@@ -1,7 +1,6 @@
using System.ComponentModel;
using DapperExample.Models;
using JetBrains.Annotations;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Queries.Expressions;
@@ -12,14 +11,14 @@ namespace DapperExample.Definitions;
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public sealed class TodoItemDefinition : JsonApiResourceDefinition
{
- private readonly IClock _clock;
+ private readonly TimeProvider _timeProvider;
- public TodoItemDefinition(IResourceGraph resourceGraph, IClock clock)
+ public TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider)
: base(resourceGraph)
{
- ArgumentGuard.NotNull(clock);
+ ArgumentNullException.ThrowIfNull(timeProvider);
- _clock = clock;
+ _timeProvider = timeProvider;
}
public override SortExpression OnApplySort(SortExpression? existingSort)
@@ -39,11 +38,11 @@ public override Task OnWritingAsync(TodoItem resource, WriteOperationKind writeO
{
if (writeOperation == WriteOperationKind.CreateResource)
{
- resource.CreatedAt = _clock.UtcNow;
+ resource.CreatedAt = _timeProvider.GetUtcNow();
}
else if (writeOperation == WriteOperationKind.UpdateResource)
{
- resource.LastModifiedAt = _clock.UtcNow;
+ resource.LastModifiedAt = _timeProvider.GetUtcNow();
}
return Task.CompletedTask;
diff --git a/src/Examples/DapperExample/FromEntitiesNavigationResolver.cs b/src/Examples/DapperExample/FromEntitiesNavigationResolver.cs
index 8ab88473c1..4ae5f05dcc 100644
--- a/src/Examples/DapperExample/FromEntitiesNavigationResolver.cs
+++ b/src/Examples/DapperExample/FromEntitiesNavigationResolver.cs
@@ -1,6 +1,5 @@
using DapperExample.Data;
using DapperExample.TranslationToSql.DataModel;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Repositories;
using Microsoft.EntityFrameworkCore;
@@ -18,9 +17,9 @@ internal sealed class FromEntitiesNavigationResolver : IInverseNavigationResolve
public FromEntitiesNavigationResolver(IResourceGraph resourceGraph, FromEntitiesDataModelService dataModelService, AppDbContext appDbContext)
{
- ArgumentGuard.NotNull(resourceGraph);
- ArgumentGuard.NotNull(dataModelService);
- ArgumentGuard.NotNull(appDbContext);
+ ArgumentNullException.ThrowIfNull(resourceGraph);
+ ArgumentNullException.ThrowIfNull(dataModelService);
+ ArgumentNullException.ThrowIfNull(appDbContext);
_defaultResolver = new InverseNavigationResolver(resourceGraph, new[]
{
diff --git a/src/Examples/DapperExample/IClock.cs b/src/Examples/DapperExample/IClock.cs
deleted file mode 100644
index 0319c42480..0000000000
--- a/src/Examples/DapperExample/IClock.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace DapperExample;
-
-public interface IClock
-{
- DateTimeOffset UtcNow { get; }
-}
diff --git a/src/Examples/DapperExample/Program.cs b/src/Examples/DapperExample/Program.cs
index 00ab54ca97..6e84497d02 100644
--- a/src/Examples/DapperExample/Program.cs
+++ b/src/Examples/DapperExample/Program.cs
@@ -11,13 +11,12 @@
using JsonApiDotNetCore.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.Extensions.DependencyInjection.Extensions;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Add services to the container.
-builder.Services.TryAddSingleton();
+builder.Services.AddSingleton();
DatabaseProvider databaseProvider = GetDatabaseProvider(builder.Configuration);
string? connectionString = builder.Configuration.GetConnectionString($"DapperExample{databaseProvider}");
@@ -31,8 +30,13 @@
}
case DatabaseProvider.MySql:
{
- builder.Services.AddMySql(connectionString, ServerVersion.AutoDetect(connectionString),
- optionsAction: options => SetDbContextDebugOptions(options));
+#if NET8_0
+ ServerVersion serverVersion = ServerVersion.AutoDetect(connectionString);
+#else
+ ServerVersion serverVersion = await ServerVersion.AutoDetectAsync(connectionString);
+#endif
+
+ builder.Services.AddMySql(connectionString, serverVersion, optionsAction: options => SetDbContextDebugOptions(options));
break;
}
@@ -86,7 +90,7 @@
await CreateDatabaseAsync(app.Services);
-app.Run();
+await app.RunAsync();
static DatabaseProvider GetDatabaseProvider(IConfiguration configuration)
{
diff --git a/src/Examples/DapperExample/Repositories/DapperFacade.cs b/src/Examples/DapperExample/Repositories/DapperFacade.cs
index 4d30e430c7..e7190ec0de 100644
--- a/src/Examples/DapperExample/Repositories/DapperFacade.cs
+++ b/src/Examples/DapperExample/Repositories/DapperFacade.cs
@@ -17,14 +17,14 @@ internal sealed class DapperFacade
public DapperFacade(IDataModelService dataModelService)
{
- ArgumentGuard.NotNull(dataModelService);
+ ArgumentNullException.ThrowIfNull(dataModelService);
_dataModelService = dataModelService;
}
public CommandDefinition GetSqlCommand(SqlTreeNode node, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(node);
+ ArgumentNullException.ThrowIfNull(node);
var queryBuilder = new SqlQueryBuilder(_dataModelService.DatabaseProvider);
string statement = queryBuilder.GetCommand(node);
@@ -36,7 +36,7 @@ public CommandDefinition GetSqlCommand(SqlTreeNode node, CancellationToken cance
public IReadOnlyCollection BuildSqlCommandsForOneToOneRelationshipsChangedToNotNull(ResourceChangeDetector changeDetector,
CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(changeDetector);
+ ArgumentNullException.ThrowIfNull(changeDetector);
List sqlCommands = [];
@@ -73,13 +73,13 @@ public IReadOnlyCollection BuildSqlCommandsForOneToOneRelatio
}
}
- return sqlCommands;
+ return sqlCommands.AsReadOnly();
}
public IReadOnlyCollection BuildSqlCommandsForChangedRelationshipsHavingForeignKeyAtRightSide(ResourceChangeDetector changeDetector,
TId leftId, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(changeDetector);
+ ArgumentNullException.ThrowIfNull(changeDetector);
List sqlCommands = [];
@@ -107,26 +107,26 @@ public IReadOnlyCollection BuildSqlCommandsForChangedRelation
object[] rightIdsToRemove = currentRightIds.Except(newRightIds).ToArray();
object[] rightIdsToAdd = newRightIds.Except(currentRightIds).ToArray();
- if (rightIdsToRemove.Any())
+ if (rightIdsToRemove.Length > 0)
{
CommandDefinition sqlCommand = BuildSqlCommandForRemoveFromToMany(foreignKey, rightIdsToRemove, cancellationToken);
sqlCommands.Add(sqlCommand);
}
- if (rightIdsToAdd.Any())
+ if (rightIdsToAdd.Length > 0)
{
CommandDefinition sqlCommand = BuildSqlCommandForAddToToMany(foreignKey, leftId!, rightIdsToAdd, cancellationToken);
sqlCommands.Add(sqlCommand);
}
}
- return sqlCommands;
+ return sqlCommands.AsReadOnly();
}
public CommandDefinition BuildSqlCommandForRemoveFromToMany(RelationshipForeignKey foreignKey, object[] rightResourceIdValues,
CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(foreignKey);
+ ArgumentNullException.ThrowIfNull(foreignKey);
ArgumentGuard.NotNullNorEmpty(rightResourceIdValues);
if (!foreignKey.IsNullable)
@@ -149,8 +149,8 @@ public CommandDefinition BuildSqlCommandForRemoveFromToMany(RelationshipForeignK
public CommandDefinition BuildSqlCommandForAddToToMany(RelationshipForeignKey foreignKey, object leftId, object[] rightResourceIdValues,
CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(foreignKey);
- ArgumentGuard.NotNull(leftId);
+ ArgumentNullException.ThrowIfNull(foreignKey);
+ ArgumentNullException.ThrowIfNull(leftId);
ArgumentGuard.NotNullNorEmpty(rightResourceIdValues);
var columnsToUpdate = new Dictionary
@@ -165,7 +165,7 @@ public CommandDefinition BuildSqlCommandForAddToToMany(RelationshipForeignKey fo
public CommandDefinition BuildSqlCommandForCreate(ResourceChangeDetector changeDetector, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(changeDetector);
+ ArgumentNullException.ThrowIfNull(changeDetector);
IReadOnlyDictionary columnsToSet = changeDetector.GetChangedColumnValues();
@@ -176,11 +176,11 @@ public CommandDefinition BuildSqlCommandForCreate(ResourceChangeDetector changeD
public CommandDefinition? BuildSqlCommandForUpdate(ResourceChangeDetector changeDetector, TId leftId, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(changeDetector);
+ ArgumentNullException.ThrowIfNull(changeDetector);
IReadOnlyDictionary columnsToUpdate = changeDetector.GetChangedColumnValues();
- if (columnsToUpdate.Any())
+ if (columnsToUpdate.Count > 0)
{
var updateBuilder = new UpdateResourceStatementBuilder(_dataModelService);
UpdateNode updateNode = updateBuilder.Build(changeDetector.ResourceType, columnsToUpdate, leftId!);
diff --git a/src/Examples/DapperExample/Repositories/DapperRepository.cs b/src/Examples/DapperExample/Repositories/DapperRepository.cs
index 0c54b34353..716db19519 100644
--- a/src/Examples/DapperExample/Repositories/DapperRepository.cs
+++ b/src/Examples/DapperExample/Repositories/DapperRepository.cs
@@ -1,3 +1,4 @@
+using System.Data;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using Dapper;
@@ -90,7 +91,7 @@ namespace DapperExample.Repositories;
///
///
///
-public sealed class DapperRepository : IResourceRepository, IRepositorySupportsTransaction
+public sealed partial class DapperRepository : IResourceRepository, IRepositorySupportsTransaction
where TResource : class, IIdentifiable
{
private readonly ITargetedFields _targetedFields;
@@ -102,7 +103,6 @@ public sealed class DapperRepository : IResourceRepository> _logger;
- private readonly CollectionConverter _collectionConverter = new();
private readonly ParameterFormatter _parameterFormatter = new();
private readonly DapperFacade _dapperFacade;
@@ -114,14 +114,14 @@ public DapperRepository(ITargetedFields targetedFields, IResourceGraph resourceG
IResourceDefinitionAccessor resourceDefinitionAccessor, AmbientTransactionFactory transactionFactory, IDataModelService dataModelService,
SqlCaptureStore captureStore, ILoggerFactory loggerFactory)
{
- ArgumentGuard.NotNull(targetedFields);
- ArgumentGuard.NotNull(resourceGraph);
- ArgumentGuard.NotNull(resourceFactory);
- ArgumentGuard.NotNull(resourceDefinitionAccessor);
- ArgumentGuard.NotNull(transactionFactory);
- ArgumentGuard.NotNull(dataModelService);
- ArgumentGuard.NotNull(captureStore);
- ArgumentGuard.NotNull(loggerFactory);
+ ArgumentNullException.ThrowIfNull(targetedFields);
+ ArgumentNullException.ThrowIfNull(resourceGraph);
+ ArgumentNullException.ThrowIfNull(resourceFactory);
+ ArgumentNullException.ThrowIfNull(resourceDefinitionAccessor);
+ ArgumentNullException.ThrowIfNull(transactionFactory);
+ ArgumentNullException.ThrowIfNull(dataModelService);
+ ArgumentNullException.ThrowIfNull(captureStore);
+ ArgumentNullException.ThrowIfNull(loggerFactory);
_targetedFields = targetedFields;
_resourceGraph = resourceGraph;
@@ -138,7 +138,7 @@ public DapperRepository(ITargetedFields targetedFields, IResourceGraph resourceG
///
public async Task> GetAsync(QueryLayer queryLayer, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(queryLayer);
+ ArgumentNullException.ThrowIfNull(queryLayer);
var mapper = new ResultSetMapper(queryLayer.Include);
@@ -180,7 +180,7 @@ public async Task CountAsync(FilterExpression? filter, CancellationToken ca
///
public Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TId id, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(resourceClrType);
+ ArgumentNullException.ThrowIfNull(resourceClrType);
var resource = (TResource)_resourceFactory.CreateInstance(resourceClrType);
resource.Id = id;
@@ -191,8 +191,8 @@ public Task GetForCreateAsync(Type resourceClrType, [DisallowNull] TI
///
public async Task CreateAsync(TResource resourceFromRequest, TResource resourceForDatabase, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(resourceFromRequest);
- ArgumentGuard.NotNull(resourceForDatabase);
+ ArgumentNullException.ThrowIfNull(resourceFromRequest);
+ ArgumentNullException.ThrowIfNull(resourceForDatabase);
var changeDetector = new ResourceChangeDetector(ResourceType, _dataModelService);
@@ -216,7 +216,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected > 1)
{
- throw new DataStoreUpdateException(new Exception("Multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Multiple rows found."));
}
}
@@ -233,7 +233,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected == 0)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist."));
}
}
}, cancellationToken);
@@ -269,12 +269,12 @@ private async Task ApplyTargetedFieldsAsync(TResource resourceFromRequest, TReso
if (relationship is HasManyAttribute hasManyRelationship)
{
- HashSet rightResourceIds = _collectionConverter.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
+ HashSet rightResourceIds = CollectionConverter.Instance.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, hasManyRelationship, rightResourceIds, writeOperation,
cancellationToken);
- return _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType);
+ return CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType);
}
return rightValue;
@@ -283,7 +283,7 @@ await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, has
///
public async Task GetForUpdateAsync(QueryLayer queryLayer, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(queryLayer);
+ ArgumentNullException.ThrowIfNull(queryLayer);
IReadOnlyCollection resources = await GetAsync(queryLayer, cancellationToken);
return resources.FirstOrDefault();
@@ -292,8 +292,8 @@ await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, has
///
public async Task UpdateAsync(TResource resourceFromRequest, TResource resourceFromDatabase, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(resourceFromRequest);
- ArgumentGuard.NotNull(resourceFromDatabase);
+ ArgumentNullException.ThrowIfNull(resourceFromRequest);
+ ArgumentNullException.ThrowIfNull(resourceFromDatabase);
var changeDetector = new ResourceChangeDetector(ResourceType, _dataModelService);
changeDetector.CaptureCurrentValues(resourceFromDatabase);
@@ -313,7 +313,7 @@ public async Task UpdateAsync(TResource resourceFromRequest, TResource resourceF
IReadOnlyCollection postSqlCommands =
_dapperFacade.BuildSqlCommandsForChangedRelationshipsHavingForeignKeyAtRightSide(changeDetector, resourceFromDatabase.Id, cancellationToken);
- if (preSqlCommands.Any() || updateCommand != null || postSqlCommands.Any())
+ if (preSqlCommands.Count > 0 || updateCommand != null || postSqlCommands.Count > 0)
{
await ExecuteInTransactionAsync(async transaction =>
{
@@ -324,7 +324,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected > 1)
{
- throw new DataStoreUpdateException(new Exception("Multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Multiple rows found."));
}
}
@@ -335,7 +335,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected != 1)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist or multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist or multiple rows found."));
}
}
@@ -346,7 +346,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected == 0)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist."));
}
}
}, cancellationToken);
@@ -364,7 +364,7 @@ public async Task DeleteAsync(TResource? resourceFromDatabase, [DisallowNull] TI
await _resourceDefinitionAccessor.OnWritingAsync(placeholderResource, WriteOperationKind.DeleteResource, cancellationToken);
var deleteBuilder = new DeleteResourceStatementBuilder(_dataModelService);
- DeleteNode deleteNode = deleteBuilder.Build(ResourceType, placeholderResource.Id!);
+ DeleteNode deleteNode = deleteBuilder.Build(ResourceType, placeholderResource.Id);
CommandDefinition sqlCommand = _dapperFacade.GetSqlCommand(deleteNode, cancellationToken);
await ExecuteInTransactionAsync(async transaction =>
@@ -374,7 +374,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected != 1)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist or multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist or multiple rows found."));
}
}, cancellationToken);
@@ -384,7 +384,7 @@ await ExecuteInTransactionAsync(async transaction =>
///
public async Task SetRelationshipAsync(TResource leftResource, object? rightValue, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(leftResource);
+ ArgumentNullException.ThrowIfNull(leftResource);
RelationshipAttribute relationship = _targetedFields.Relationships.Single();
@@ -409,7 +409,7 @@ public async Task SetRelationshipAsync(TResource leftResource, object? rightValu
IReadOnlyCollection postSqlCommands =
_dapperFacade.BuildSqlCommandsForChangedRelationshipsHavingForeignKeyAtRightSide(changeDetector, leftResource.Id, cancellationToken);
- if (preSqlCommands.Any() || updateCommand != null || postSqlCommands.Any())
+ if (preSqlCommands.Count > 0 || updateCommand != null || postSqlCommands.Count > 0)
{
await ExecuteInTransactionAsync(async transaction =>
{
@@ -420,7 +420,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected > 1)
{
- throw new DataStoreUpdateException(new Exception("Multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Multiple rows found."));
}
}
@@ -431,7 +431,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected != 1)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist or multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist or multiple rows found."));
}
}
@@ -442,7 +442,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected == 0)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist."));
}
}
}, cancellationToken);
@@ -455,7 +455,7 @@ await ExecuteInTransactionAsync(async transaction =>
public async Task AddToToManyRelationshipAsync(TResource? leftResource, [DisallowNull] TId leftId, ISet rightResourceIds,
CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(rightResourceIds);
+ ArgumentNullException.ThrowIfNull(rightResourceIds);
var relationship = (HasManyAttribute)_targetedFields.Relationships.Single();
@@ -463,11 +463,13 @@ public async Task AddToToManyRelationshipAsync(TResource? leftResource, [Disallo
leftPlaceholderResource.Id = leftId;
await _resourceDefinitionAccessor.OnAddToRelationshipAsync(leftPlaceholderResource, relationship, rightResourceIds, cancellationToken);
- relationship.SetValue(leftPlaceholderResource, _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
+
+ relationship.SetValue(leftPlaceholderResource,
+ CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
await _resourceDefinitionAccessor.OnWritingAsync(leftPlaceholderResource, WriteOperationKind.AddToRelationship, cancellationToken);
- if (rightResourceIds.Any())
+ if (rightResourceIds.Count > 0)
{
RelationshipForeignKey foreignKey = _dataModelService.GetForeignKey(relationship);
object[] rightResourceIdValues = rightResourceIds.Select(resource => resource.GetTypedId()).ToArray();
@@ -482,7 +484,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected != rightResourceIdValues.Length)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist or multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist or multiple rows found."));
}
}, cancellationToken);
@@ -493,17 +495,17 @@ await ExecuteInTransactionAsync(async transaction =>
///
public async Task RemoveFromToManyRelationshipAsync(TResource leftResource, ISet rightResourceIds, CancellationToken cancellationToken)
{
- ArgumentGuard.NotNull(leftResource);
- ArgumentGuard.NotNull(rightResourceIds);
+ ArgumentNullException.ThrowIfNull(leftResource);
+ ArgumentNullException.ThrowIfNull(rightResourceIds);
var relationship = (HasManyAttribute)_targetedFields.Relationships.Single();
await _resourceDefinitionAccessor.OnRemoveFromRelationshipAsync(leftResource, relationship, rightResourceIds, cancellationToken);
- relationship.SetValue(leftResource, _collectionConverter.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
+ relationship.SetValue(leftResource, CollectionConverter.Instance.CopyToTypedCollection(rightResourceIds, relationship.Property.PropertyType));
await _resourceDefinitionAccessor.OnWritingAsync(leftResource, WriteOperationKind.RemoveFromRelationship, cancellationToken);
- if (rightResourceIds.Any())
+ if (rightResourceIds.Count > 0)
{
RelationshipForeignKey foreignKey = _dataModelService.GetForeignKey(relationship);
object[] rightResourceIdValues = rightResourceIds.Select(resource => resource.GetTypedId()).ToArray();
@@ -516,7 +518,7 @@ await ExecuteInTransactionAsync(async transaction =>
if (rowsAffected != rightResourceIdValues.Length)
{
- throw new DataStoreUpdateException(new Exception("Row does not exist or multiple rows found."));
+ throw new DataStoreUpdateException(new DataException("Row does not exist or multiple rows found."));
}
}, cancellationToken);
@@ -530,19 +532,18 @@ private void LogSqlCommand(CommandDefinition command)
_captureStore.Add(command.CommandText, parameters);
- string message = GetLogText(command.CommandText, parameters);
- _logger.LogInformation(message);
- }
-
- private string GetLogText(string statement, IDictionary? parameters)
- {
- if (parameters?.Any() == true)
+ if (_logger.IsEnabled(LogLevel.Information))
{
- string parametersText = string.Join(", ", parameters.Select(parameter => _parameterFormatter.Format(parameter.Key, parameter.Value)));
- return $"Executing SQL with parameters: {parametersText}{Environment.NewLine}{statement}";
+ if (parameters?.Count > 0)
+ {
+ string parametersText = string.Join(", ", parameters.Select(parameter => _parameterFormatter.Format(parameter.Key, parameter.Value)));
+ LogExecuteWithParameters(Environment.NewLine, command.CommandText, parametersText);
+ }
+ else
+ {
+ LogExecute(Environment.NewLine, command.CommandText);
+ }
}
-
- return $"Executing SQL: {Environment.NewLine}{statement}";
}
private async Task ExecuteQueryAsync(Func> asyncAction, CancellationToken cancellationToken)
@@ -580,4 +581,10 @@ private async Task ExecuteInTransactionAsync(Func asyncActi
throw new DataStoreUpdateException(exception);
}
}
+
+ [LoggerMessage(Level = LogLevel.Information, SkipEnabledCheck = true, Message = "Executing SQL: {LineBreak}{Query}")]
+ private partial void LogExecute(string lineBreak, string query);
+
+ [LoggerMessage(Level = LogLevel.Information, SkipEnabledCheck = true, Message = "Executing SQL with parameters: {Parameters}{LineBreak}{Query}")]
+ private partial void LogExecuteWithParameters(string lineBreak, string query, string parameters);
}
diff --git a/src/Examples/DapperExample/Repositories/ResourceChangeDetector.cs b/src/Examples/DapperExample/Repositories/ResourceChangeDetector.cs
index 1d9b998340..58f7579254 100644
--- a/src/Examples/DapperExample/Repositories/ResourceChangeDetector.cs
+++ b/src/Examples/DapperExample/Repositories/ResourceChangeDetector.cs
@@ -12,21 +12,20 @@ namespace DapperExample.Repositories;
///
internal sealed class ResourceChangeDetector
{
- private readonly CollectionConverter _collectionConverter = new();
private readonly IDataModelService _dataModelService;
private Dictionary _currentColumnValues = [];
private Dictionary _newColumnValues = [];
- private Dictionary> _currentRightResourcesByRelationship = [];
- private Dictionary> _newRightResourcesByRelationship = [];
+ private Dictionary> _currentRightResourcesByRelationship = [];
+ private Dictionary> _newRightResourcesByRelationship = [];
public ResourceType ResourceType { get; }
public ResourceChangeDetector(ResourceType resourceType, IDataModelService dataModelService)
{
- ArgumentGuard.NotNull(resourceType);
- ArgumentGuard.NotNull(dataModelService);
+ ArgumentNullException.ThrowIfNull(resourceType);
+ ArgumentNullException.ThrowIfNull(dataModelService);
ResourceType = resourceType;
_dataModelService = dataModelService;
@@ -34,7 +33,7 @@ public ResourceChangeDetector(ResourceType resourceType, IDataModelService dataM
public void CaptureCurrentValues(IIdentifiable resource)
{
- ArgumentGuard.NotNull(resource);
+ ArgumentNullException.ThrowIfNull(resource);
AssertSameType(ResourceType, resource);
_currentColumnValues = CaptureColumnValues(resource);
@@ -43,7 +42,7 @@ public void CaptureCurrentValues(IIdentifiable resource)
public void CaptureNewValues(IIdentifiable resource)
{
- ArgumentGuard.NotNull(resource);
+ ArgumentNullException.ThrowIfNull(resource);
AssertSameType(ResourceType, resource);
_newColumnValues = CaptureColumnValues(resource);
@@ -62,14 +61,14 @@ public void CaptureNewValues(IIdentifiable resource)
return columnValues;
}
- private Dictionary> CaptureRightResourcesByRelationship(IIdentifiable resource)
+ private Dictionary> CaptureRightResourcesByRelationship(IIdentifiable resource)
{
- Dictionary> relationshipValues = [];
+ Dictionary> relationshipValues = [];
foreach (RelationshipAttribute relationship in ResourceType.Relationships)
{
object? rightValue = relationship.GetValue(resource);
- HashSet rightResources = _collectionConverter.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
+ HashSet rightResources = CollectionConverter.Instance.ExtractResources(rightValue).ToHashSet(IdentifiableComparer.Instance);
relationshipValues[relationship] = rightResources;
}
@@ -88,7 +87,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
if (!foreignKey.IsNullable)
{
object? currentRightId =
- _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out ISet? currentRightResources)
+ _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out HashSet? currentRightResources)
? currentRightResources.FirstOrDefault()?.GetTypedId()
: null;
@@ -118,7 +117,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
if (newRightId != null)
{
object? currentRightId =
- _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out ISet? currentRightResources)
+ _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out HashSet? currentRightResources)
? currentRightResources.FirstOrDefault()?.GetTypedId()
: null;
@@ -130,7 +129,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
}
}
- return changes;
+ return changes.AsReadOnly();
}
public IReadOnlyDictionary GetChangedColumnValues()
@@ -147,7 +146,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
}
}
- return changes;
+ return changes.AsReadOnly();
}
public IReadOnlyDictionary GetChangedToOneRelationshipsWithForeignKeyAtRightSide()
@@ -165,7 +164,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
continue;
}
- object? currentRightId = _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out ISet? currentRightResources)
+ object? currentRightId = _currentRightResourcesByRelationship.TryGetValue(hasOneRelationship, out HashSet? currentRightResources)
? currentRightResources.FirstOrDefault()?.GetTypedId()
: null;
@@ -178,7 +177,7 @@ public void AssertIsNotClearingAnyRequiredToOneRelationships(string resourceName
}
}
- return changes;
+ return changes.AsReadOnly();
}
public IReadOnlyDictionary currentRightIds, ISet
/// The reference to t1 in the WHERE clause has become stale and needs to be pulled out into scope, which is t2.
///
-internal sealed class StaleColumnReferenceRewriter : SqlTreeNodeVisitor
+internal sealed partial class StaleColumnReferenceRewriter : SqlTreeNodeVisitor
{
private readonly IReadOnlyDictionary _oldToNewTableAliasMap;
private readonly ILogger _logger;
@@ -36,8 +36,8 @@ internal sealed class StaleColumnReferenceRewriter : SqlTreeNodeVisitor oldToNewTableAliasMap, ILoggerFactory loggerFactory)
{
- ArgumentGuard.NotNull(oldToNewTableAliasMap);
- ArgumentGuard.NotNull(loggerFactory);
+ ArgumentNullException.ThrowIfNull(oldToNewTableAliasMap);
+ ArgumentNullException.ThrowIfNull(loggerFactory);
_oldToNewTableAliasMap = oldToNewTableAliasMap;
_logger = loggerFactory.CreateLogger();
@@ -59,12 +59,13 @@ public override SqlTreeNode VisitSelect(SelectNode node, ColumnVisitMode mode)
{
IncludeTableAliasInCurrentScope(node);
- using IDisposable scope = EnterSelectScope();
-
- IReadOnlyDictionary> selectors = VisitSelectors(node.Selectors, mode);
- WhereNode? where = TypedVisit(node.Where, mode);
- OrderByNode? orderBy = TypedVisit(node.OrderBy, mode);
- return new SelectNode(selectors, where, orderBy, node.Alias);
+ using (EnterSelectScope())
+ {
+ ReadOnlyDictionary> selectors = VisitSelectors(node.Selectors, mode);
+ WhereNode? where = TypedVisit(node.Where, mode);
+ OrderByNode? orderBy = TypedVisit(node.OrderBy, mode);
+ return new SelectNode(selectors, where, orderBy, node.Alias);
+ }
}
private void IncludeTableAliasInCurrentScope(TableSourceNode tableSource)
@@ -76,7 +77,7 @@ private void IncludeTableAliasInCurrentScope(TableSourceNode tableSource)
}
}
- private IDisposable EnterSelectScope()
+ private PopStackOnDispose> EnterSelectScope()
{
Dictionary newScope = CopyTopStackElement();
_tablesInScopeStack.Push(newScope);
@@ -95,7 +96,7 @@ private Dictionary CopyTopStackElement()
return new Dictionary(topElement);
}
- private IReadOnlyDictionary> VisitSelectors(
+ private ReadOnlyDictionary> VisitSelectors(
IReadOnlyDictionary> selectors, ColumnVisitMode mode)
{
Dictionary> newSelectors = [];
@@ -103,12 +104,12 @@ private IReadOnlyDictionary> Visi
foreach ((TableAccessorNode tableAccessor, IReadOnlyList tableSelectors) in selectors)
{
TableAccessorNode newTableAccessor = TypedVisit(tableAccessor, mode);
- IReadOnlyList newTableSelectors = VisitList(tableSelectors, ColumnVisitMode.Declaration);
+ ReadOnlyCollection newTableSelectors = VisitSequence(tableSelectors, ColumnVisitMode.Declaration);
newSelectors.Add(newTableAccessor, newTableSelectors);
}
- return newSelectors;
+ return newSelectors.AsReadOnly();
}
public override SqlTreeNode VisitTable(TableNode node, ColumnVisitMode mode)
@@ -142,7 +143,7 @@ public override SqlTreeNode VisitColumnInTable(ColumnInTableNode node, ColumnVis
return MapColumnInTable(node, tablesInScope);
}
- private ColumnNode MapColumnInTable(ColumnInTableNode column, IDictionary tablesInScope)
+ private ColumnNode MapColumnInTable(ColumnInTableNode column, Dictionary tablesInScope)
{
if (column.TableAlias != null && !tablesInScope.ContainsKey(column.TableAlias))
{
@@ -159,7 +160,7 @@ private ColumnNode MapColumnInTable(ColumnInTableNode column, IDictionary terms = VisitList(node.Terms, mode);
+ ReadOnlyCollection terms = VisitSequence(node.Terms, mode);
return new LogicalNode(node.Operator, terms);
}
@@ -233,7 +234,7 @@ public override SqlTreeNode VisitLike(LikeNode node, ColumnVisitMode mode)
public override SqlTreeNode VisitIn(InNode node, ColumnVisitMode mode)
{
ColumnNode column = TypedVisit(node.Column, mode);
- IReadOnlyList values = VisitList(node.Values, mode);
+ ReadOnlyCollection values = VisitSequence(node.Values, mode);
return new InNode(column, values);
}
@@ -251,7 +252,7 @@ public override SqlTreeNode VisitCount(CountNode node, ColumnVisitMode mode)
public override SqlTreeNode VisitOrderBy(OrderByNode node, ColumnVisitMode mode)
{
- IReadOnlyList terms = VisitList(node.Terms, mode);
+ ReadOnlyCollection terms = VisitSequence(node.Terms, mode);
return new OrderByNode(terms);
}
@@ -277,19 +278,22 @@ public override SqlTreeNode VisitNullConstant(NullConstantNode node, ColumnVisit
return node;
}
- [return: NotNullIfNotNull("node")]
+ [return: NotNullIfNotNull(nameof(node))]
private T? TypedVisit(T? node, ColumnVisitMode mode)
where T : SqlTreeNode
{
return node != null ? (T)Visit(node, mode) : null;
}
- private IReadOnlyList VisitList(IEnumerable nodes, ColumnVisitMode mode)
+ private ReadOnlyCollection VisitSequence(IEnumerable nodes, ColumnVisitMode mode)
where T : SqlTreeNode
{
- return nodes.Select(element => TypedVisit(element, mode)).ToList();
+ return nodes.Select(element => TypedVisit(element, mode)).ToArray().AsReadOnly();
}
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Mapped inaccessible column {FromColumn} to {ToColumn}.")]
+ private partial void LogColumnMapped(ColumnNode fromColumn, ColumnNode toColumn);
+
private sealed class PopStackOnDispose(Stack stack) : IDisposable
{
private readonly Stack _stack = stack;
diff --git a/src/Examples/DapperExample/TranslationToSql/Transformations/UnusedSelectorsRewriter.cs b/src/Examples/DapperExample/TranslationToSql/Transformations/UnusedSelectorsRewriter.cs
index 7cffc8e29a..fe0f326209 100644
--- a/src/Examples/DapperExample/TranslationToSql/Transformations/UnusedSelectorsRewriter.cs
+++ b/src/Examples/DapperExample/TranslationToSql/Transformations/UnusedSelectorsRewriter.cs
@@ -1,6 +1,6 @@
+using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using DapperExample.TranslationToSql.TreeNodes;
-using JsonApiDotNetCore;
namespace DapperExample.TranslationToSql.Transformations;
@@ -26,7 +26,7 @@ namespace DapperExample.TranslationToSql.Transformations;
///
/// The selectors t1."AccountId" and t1."FirstName" have no references and can be removed.
///
-internal sealed class UnusedSelectorsRewriter : SqlTreeNodeVisitor, SqlTreeNode>
+internal sealed partial class UnusedSelectorsRewriter : SqlTreeNodeVisitor, SqlTreeNode>
{
private readonly ColumnSelectorUsageCollector _usageCollector;
private readonly ILogger _logger;
@@ -35,7 +35,7 @@ internal sealed class UnusedSelectorsRewriter : SqlTreeNodeVisitor();
@@ -43,7 +43,7 @@ public UnusedSelectorsRewriter(ILoggerFactory loggerFactory)
public SelectNode RemoveUnusedSelectorsInSubQueries(SelectNode select)
{
- ArgumentGuard.NotNull(select);
+ ArgumentNullException.ThrowIfNull(select);
_rootSelect = select;
@@ -52,9 +52,9 @@ public SelectNode RemoveUnusedSelectorsInSubQueries(SelectNode select)
_hasChanged = false;
_usageCollector.Collect(_rootSelect);
- _logger.LogDebug("Started removal of unused selectors.");
+ LogStarted();
_rootSelect = TypedVisit(_rootSelect, _usageCollector.UsedColumns);
- _logger.LogDebug("Finished removal of unused selectors.");
+ LogFinished();
}
while (_hasChanged);
@@ -68,13 +68,13 @@ public override SqlTreeNode DefaultVisit(SqlTreeNode node, ISet used
public override SqlTreeNode VisitSelect(SelectNode node, ISet usedColumns)
{
- IReadOnlyDictionary> selectors = VisitSelectors(node, usedColumns);
+ ReadOnlyDictionary> selectors = VisitSelectors(node, usedColumns);
WhereNode? where = TypedVisit(node.Where, usedColumns);
OrderByNode? orderBy = TypedVisit(node.OrderBy, usedColumns);
return new SelectNode(selectors, where, orderBy, node.Alias);
}
- private IReadOnlyDictionary> VisitSelectors(SelectNode select, ISet usedColumns)
+ private ReadOnlyDictionary> VisitSelectors(SelectNode select, ISet usedColumns)
{
Dictionary> newSelectors = [];
@@ -85,10 +85,10 @@ private IReadOnlyDictionary> Visi
newSelectors.Add(newTableAccessor, newTableSelectors);
}
- return newSelectors;
+ return newSelectors.AsReadOnly();
}
- private List VisitTableSelectors(IEnumerable selectors, ISet usedColumns)
+ private ReadOnlyCollection VisitTableSelectors(IEnumerable selectors, ISet usedColumns)
{
List newTableSelectors = [];
@@ -98,7 +98,7 @@ private List VisitTableSelectors(IEnumerable selecto
{
if (!usedColumns.Contains(columnSelector.Column))
{
- _logger.LogDebug($"Removing unused selector {columnSelector}.");
+ LogSelectorRemoved(columnSelector);
_hasChanged = true;
continue;
}
@@ -107,7 +107,7 @@ private List VisitTableSelectors(IEnumerable selecto
newTableSelectors.Add(selector);
}
- return newTableSelectors;
+ return newTableSelectors.AsReadOnly();
}
public override SqlTreeNode VisitFrom(FromNode node, ISet usedColumns)
@@ -150,7 +150,7 @@ public override SqlTreeNode VisitNot(NotNode node, ISet usedColumns)
public override SqlTreeNode VisitLogical(LogicalNode node, ISet usedColumns)
{
- IReadOnlyList terms = VisitList(node.Terms, usedColumns);
+ ReadOnlyCollection terms = VisitSequence(node.Terms, usedColumns);
return new LogicalNode(node.Operator, terms);
}
@@ -170,7 +170,7 @@ public override SqlTreeNode VisitLike(LikeNode node, ISet usedColumn
public override SqlTreeNode VisitIn(InNode node, ISet usedColumns)
{
ColumnNode column = TypedVisit(node.Column, usedColumns);
- IReadOnlyList values = VisitList(node.Values, usedColumns);
+ ReadOnlyCollection values = VisitSequence(node.Values, usedColumns);
return new InNode(column, values);
}
@@ -188,7 +188,7 @@ public override SqlTreeNode VisitCount(CountNode node, ISet usedColu
public override SqlTreeNode VisitOrderBy(OrderByNode node, ISet usedColumns)
{
- IReadOnlyList terms = VisitList(node.Terms, usedColumns);
+ ReadOnlyCollection terms = VisitSequence(node.Terms, usedColumns);
return new OrderByNode(terms);
}
@@ -204,16 +204,25 @@ public override SqlTreeNode VisitOrderByCount(OrderByCountNode node, ISet(T? node, ISet usedColumns)
where T : SqlTreeNode
{
return node != null ? (T)Visit(node, usedColumns) : null;
}
- private IReadOnlyList VisitList(IEnumerable nodes, ISet usedColumns)
+ private ReadOnlyCollection VisitSequence(IEnumerable nodes, ISet usedColumns)
where T : SqlTreeNode
{
- return nodes.Select(element => TypedVisit(element, usedColumns)).ToList();
+ return nodes.Select(element => TypedVisit(element, usedColumns)).ToArray().AsReadOnly();
}
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Started removal of unused selectors.")]
+ private partial void LogStarted();
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Finished removal of unused selectors.")]
+ private partial void LogFinished();
+
+ [LoggerMessage(Level = LogLevel.Debug, Message = "Removing unused selector {Selector}.")]
+ private partial void LogSelectorRemoved(ColumnSelectorNode selector);
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnAssignmentNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnAssignmentNode.cs
index 1884dc8dbf..3578ea9a55 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnAssignmentNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnAssignmentNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -17,8 +15,8 @@ internal sealed class ColumnAssignmentNode : SqlTreeNode
public ColumnAssignmentNode(ColumnNode column, SqlValueNode value)
{
- ArgumentGuard.NotNull(column);
- ArgumentGuard.NotNull(value);
+ ArgumentNullException.ThrowIfNull(column);
+ ArgumentNullException.ThrowIfNull(value);
Column = column;
Value = value;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInSelectNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInSelectNode.cs
index 2be0561011..14d82bb1d3 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInSelectNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInSelectNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -10,8 +8,8 @@ namespace DapperExample.TranslationToSql.TreeNodes;
/// SELECT t2.Id AS Id0 FROM (SELECT t1.Id FROM Users AS t1) AS t2
/// ]]>.
///
-internal sealed class ColumnInSelectNode(ColumnSelectorNode selector, string? tableAlias) : ColumnNode(GetColumnName(selector), selector.Column.Type,
- tableAlias)
+internal sealed class ColumnInSelectNode(ColumnSelectorNode selector, string? tableAlias)
+ : ColumnNode(GetColumnName(selector), selector.Column.Type, tableAlias)
{
public ColumnSelectorNode Selector { get; } = selector;
@@ -19,7 +17,7 @@ internal sealed class ColumnInSelectNode(ColumnSelectorNode selector, string? ta
private static string GetColumnName(ColumnSelectorNode selector)
{
- ArgumentGuard.NotNull(selector);
+ ArgumentNullException.ThrowIfNull(selector);
return selector.Identity;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInTableNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInTableNode.cs
index cd605e72a4..da29ed97fc 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInTableNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnInTableNode.cs
@@ -8,7 +8,8 @@ namespace DapperExample.TranslationToSql.TreeNodes;
/// FROM Users AS t1
/// ]]>.
///
-internal sealed class ColumnInTableNode(string name, ColumnType type, string? tableAlias) : ColumnNode(name, type, tableAlias)
+internal sealed class ColumnInTableNode(string name, ColumnType type, string? tableAlias)
+ : ColumnNode(name, type, tableAlias)
{
public override TResult Accept(SqlTreeNodeVisitor visitor, TArgument argument)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnNode.cs
index e4fbcf14e6..da348882a9 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -13,7 +11,7 @@ internal abstract class ColumnNode : SqlValueNode
protected ColumnNode(string name, ColumnType type, string? tableAlias)
{
- ArgumentGuard.NotNullNorEmpty(name);
+ ArgumentException.ThrowIfNullOrEmpty(name);
Name = name;
Type = type;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnSelectorNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnSelectorNode.cs
index ab2ab1031f..6c8bb9e36a 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnSelectorNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ColumnSelectorNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -19,7 +17,7 @@ internal sealed class ColumnSelectorNode : SelectorNode
public ColumnSelectorNode(ColumnNode column, string? alias)
: base(alias)
{
- ArgumentGuard.NotNull(column);
+ ArgumentNullException.ThrowIfNull(column);
Column = column;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ComparisonNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ComparisonNode.cs
index dbf61d5451..7264189a6d 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ComparisonNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ComparisonNode.cs
@@ -1,4 +1,3 @@
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Queries.Expressions;
namespace DapperExample.TranslationToSql.TreeNodes;
@@ -16,8 +15,8 @@ internal sealed class ComparisonNode : FilterNode
public ComparisonNode(ComparisonOperator @operator, SqlValueNode left, SqlValueNode right)
{
- ArgumentGuard.NotNull(left);
- ArgumentGuard.NotNull(right);
+ ArgumentNullException.ThrowIfNull(left);
+ ArgumentNullException.ThrowIfNull(right);
Operator = @operator;
Left = left;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountNode.cs
index 07182d036f..9133969961 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -16,7 +14,7 @@ internal sealed class CountNode : SqlValueNode
public CountNode(SelectNode subSelect)
{
- ArgumentGuard.NotNull(subSelect);
+ ArgumentNullException.ThrowIfNull(subSelect);
SubSelect = subSelect;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountSelectorNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountSelectorNode.cs
index d0b9e18ca2..d344659393 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountSelectorNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/CountSelectorNode.cs
@@ -8,7 +8,8 @@ namespace DapperExample.TranslationToSql.TreeNodes;
/// SELECT COUNT(*) FROM Users
/// ]]>.
///
-internal sealed class CountSelectorNode(string? alias) : SelectorNode(alias)
+internal sealed class CountSelectorNode(string? alias)
+ : SelectorNode(alias)
{
public override TResult Accept(SqlTreeNodeVisitor visitor, TArgument argument)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/DeleteNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/DeleteNode.cs
index aa3968f872..512e0e7321 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/DeleteNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/DeleteNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -14,8 +12,8 @@ internal sealed class DeleteNode : SqlTreeNode
public DeleteNode(TableNode table, WhereNode where)
{
- ArgumentGuard.NotNull(table);
- ArgumentGuard.NotNull(where);
+ ArgumentNullException.ThrowIfNull(table);
+ ArgumentNullException.ThrowIfNull(where);
Table = table;
Where = where;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ExistsNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ExistsNode.cs
index b73882122c..96087886f9 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ExistsNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ExistsNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -16,7 +14,7 @@ internal sealed class ExistsNode : FilterNode
public ExistsNode(SelectNode subSelect)
{
- ArgumentGuard.NotNull(subSelect);
+ ArgumentNullException.ThrowIfNull(subSelect);
SubSelect = subSelect;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/FromNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/FromNode.cs
index 3d29636212..9e39a9d516 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/FromNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/FromNode.cs
@@ -5,7 +5,8 @@ namespace DapperExample.TranslationToSql.TreeNodes;
/// FROM Customers AS t1
/// ]]>.
///
-internal sealed class FromNode(TableSourceNode source) : TableAccessorNode(source)
+internal sealed class FromNode(TableSourceNode source)
+ : TableAccessorNode(source)
{
public override TResult Accept(SqlTreeNodeVisitor visitor, TArgument argument)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/InNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/InNode.cs
index 26d3c2ec47..114a141eaa 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/InNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/InNode.cs
@@ -14,7 +14,7 @@ internal sealed class InNode : FilterNode
public InNode(ColumnNode column, IReadOnlyList values)
{
- ArgumentGuard.NotNull(column);
+ ArgumentNullException.ThrowIfNull(column);
ArgumentGuard.NotNullNorEmpty(values);
Column = column;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/InsertNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/InsertNode.cs
index 8ed6770136..f85857f677 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/InsertNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/InsertNode.cs
@@ -14,7 +14,7 @@ internal sealed class InsertNode : SqlTreeNode
public InsertNode(TableNode table, IReadOnlyCollection assignments)
{
- ArgumentGuard.NotNull(table);
+ ArgumentNullException.ThrowIfNull(table);
ArgumentGuard.NotNullNorEmpty(assignments);
Table = table;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/JoinNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/JoinNode.cs
index 6ed2e4c73c..48140a0c04 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/JoinNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/JoinNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -16,8 +14,8 @@ internal sealed class JoinNode : TableAccessorNode
public JoinNode(JoinType joinType, TableSourceNode source, ColumnNode outerColumn, ColumnNode innerColumn)
: base(source)
{
- ArgumentGuard.NotNull(outerColumn);
- ArgumentGuard.NotNull(innerColumn);
+ ArgumentNullException.ThrowIfNull(outerColumn);
+ ArgumentNullException.ThrowIfNull(innerColumn);
JoinType = joinType;
OuterColumn = outerColumn;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/LikeNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/LikeNode.cs
index 034e5c012e..f713e687fc 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/LikeNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/LikeNode.cs
@@ -1,4 +1,3 @@
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Queries.Expressions;
namespace DapperExample.TranslationToSql.TreeNodes;
@@ -16,8 +15,8 @@ internal sealed class LikeNode : FilterNode
public LikeNode(ColumnNode column, TextMatchKind matchKind, string text)
{
- ArgumentGuard.NotNull(column);
- ArgumentGuard.NotNull(text);
+ ArgumentNullException.ThrowIfNull(column);
+ ArgumentNullException.ThrowIfNull(text);
Column = column;
MatchKind = matchKind;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/LogicalNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/LogicalNode.cs
index 40fc95b88c..ebf0554167 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/LogicalNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/LogicalNode.cs
@@ -1,4 +1,3 @@
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Queries.Expressions;
namespace DapperExample.TranslationToSql.TreeNodes;
@@ -14,13 +13,13 @@ internal sealed class LogicalNode : FilterNode
public IReadOnlyList Terms { get; }
public LogicalNode(LogicalOperator @operator, params FilterNode[] terms)
- : this(@operator, terms.ToList())
+ : this(@operator, terms.AsReadOnly())
{
}
public LogicalNode(LogicalOperator @operator, IReadOnlyList terms)
{
- ArgumentGuard.NotNull(terms);
+ ArgumentNullException.ThrowIfNull(terms);
if (terms.Count < 2)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/NotNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/NotNode.cs
index 38c5d80f26..f1f2e9ed22 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/NotNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/NotNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -13,7 +11,7 @@ internal sealed class NotNode : FilterNode
public NotNode(FilterNode child)
{
- ArgumentGuard.NotNull(child);
+ ArgumentNullException.ThrowIfNull(child);
Child = child;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OneSelectorNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OneSelectorNode.cs
index a9c05301a7..ac9e75d44d 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OneSelectorNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OneSelectorNode.cs
@@ -8,7 +8,8 @@ namespace DapperExample.TranslationToSql.TreeNodes;
/// SELECT 1 FROM Users
/// ]]>.
///
-internal sealed class OneSelectorNode(string? alias) : SelectorNode(alias)
+internal sealed class OneSelectorNode(string? alias)
+ : SelectorNode(alias)
{
public override TResult Accept(SqlTreeNodeVisitor visitor, TArgument argument)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByColumnNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByColumnNode.cs
index 372b1e86ff..a62094ae5b 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByColumnNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByColumnNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -17,7 +15,7 @@ internal sealed class OrderByColumnNode : OrderByTermNode
public OrderByColumnNode(ColumnNode column, bool isAscending)
: base(isAscending)
{
- ArgumentGuard.NotNull(column);
+ ArgumentNullException.ThrowIfNull(column);
Column = column;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByCountNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByCountNode.cs
index 3d8f8c240a..c044d98ba9 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByCountNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/OrderByCountNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -17,7 +15,7 @@ internal sealed class OrderByCountNode : OrderByTermNode
public OrderByCountNode(CountNode count, bool isAscending)
: base(isAscending)
{
- ArgumentGuard.NotNull(count);
+ ArgumentNullException.ThrowIfNull(count);
Count = count;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ParameterNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ParameterNode.cs
index c2a5824f72..6e206fa04d 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/ParameterNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/ParameterNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -16,7 +14,7 @@ internal sealed class ParameterNode : SqlValueNode
public ParameterNode(string name, object? value)
{
- ArgumentGuard.NotNull(name);
+ ArgumentException.ThrowIfNullOrEmpty(name);
if (!name.StartsWith('@') || name.Length < 2)
{
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/SelectNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/SelectNode.cs
index 0fc42b1ba0..add1ddc433 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/SelectNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/SelectNode.cs
@@ -19,7 +19,7 @@ internal sealed class SelectNode : TableSourceNode
public WhereNode? Where { get; }
public OrderByNode? OrderBy { get; }
- public override IReadOnlyList Columns => _columns;
+ public override IReadOnlyList Columns => _columns.AsReadOnly();
public SelectNode(IReadOnlyDictionary> selectors, WhereNode? where, OrderByNode? orderBy, string? alias)
: base(alias)
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableAccessorNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableAccessorNode.cs
index 4096789919..2822749230 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableAccessorNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableAccessorNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -11,7 +9,7 @@ internal abstract class TableAccessorNode : SqlTreeNode
protected TableAccessorNode(TableSourceNode source)
{
- ArgumentGuard.NotNull(source);
+ ArgumentNullException.ThrowIfNull(source);
Source = source;
}
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableNode.cs
index 31977f1546..9fca1971cb 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/TableNode.cs
@@ -1,5 +1,4 @@
using Humanizer;
-using JsonApiDotNetCore;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Resources.Annotations;
@@ -21,13 +20,13 @@ internal sealed class TableNode : TableSourceNode
public string Name => _resourceType.ClrType.Name.Pluralize();
- public override IReadOnlyList Columns => _columns;
+ public override IReadOnlyList Columns => _columns.AsReadOnly();
public TableNode(ResourceType resourceType, IReadOnlyDictionary columnMappings, string? alias)
: base(alias)
{
- ArgumentGuard.NotNull(resourceType);
- ArgumentGuard.NotNull(columnMappings);
+ ArgumentNullException.ThrowIfNull(resourceType);
+ ArgumentNullException.ThrowIfNull(columnMappings);
_resourceType = resourceType;
_columnMappings = columnMappings;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/UpdateNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/UpdateNode.cs
index 3aa5dbdf73..7f9b1b79a4 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/UpdateNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/UpdateNode.cs
@@ -15,9 +15,9 @@ internal sealed class UpdateNode : SqlTreeNode
public UpdateNode(TableNode table, IReadOnlyCollection assignments, WhereNode where)
{
- ArgumentGuard.NotNull(table);
+ ArgumentNullException.ThrowIfNull(table);
ArgumentGuard.NotNullNorEmpty(assignments);
- ArgumentGuard.NotNull(where);
+ ArgumentNullException.ThrowIfNull(where);
Table = table;
Assignments = assignments;
diff --git a/src/Examples/DapperExample/TranslationToSql/TreeNodes/WhereNode.cs b/src/Examples/DapperExample/TranslationToSql/TreeNodes/WhereNode.cs
index d8d72601c5..4e8c4d54e8 100644
--- a/src/Examples/DapperExample/TranslationToSql/TreeNodes/WhereNode.cs
+++ b/src/Examples/DapperExample/TranslationToSql/TreeNodes/WhereNode.cs
@@ -1,5 +1,3 @@
-using JsonApiDotNetCore;
-
namespace DapperExample.TranslationToSql.TreeNodes;
///
@@ -13,7 +11,7 @@ internal sealed class WhereNode : SqlTreeNode
public WhereNode(FilterNode filter)
{
- ArgumentGuard.NotNull(filter);
+ ArgumentNullException.ThrowIfNull(filter);
Filter = filter;
}
diff --git a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj
index 0ccb4bbc5f..3edc993428 100644
--- a/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj
+++ b/src/Examples/DatabasePerTenantExample/DatabasePerTenantExample.csproj
@@ -1,6 +1,6 @@
- net8.0;net6.0
+ net9.0;net8.0
diff --git a/src/Examples/DatabasePerTenantExample/Program.cs b/src/Examples/DatabasePerTenantExample/Program.cs
index 1414e28424..4b88357d78 100644
--- a/src/Examples/DatabasePerTenantExample/Program.cs
+++ b/src/Examples/DatabasePerTenantExample/Program.cs
@@ -38,7 +38,7 @@
await CreateDatabaseAsync("AdventureWorks", app.Services);
await CreateDatabaseAsync("Contoso", app.Services);
-app.Run();
+await app.RunAsync();
[Conditional("DEBUG")]
static void SetDbContextDebugOptions(DbContextOptionsBuilder options)
diff --git a/src/Examples/GettingStarted/Data/SampleDbContext.cs b/src/Examples/GettingStarted/Data/SampleDbContext.cs
index 5e65f8466e..cd8b16515d 100644
--- a/src/Examples/GettingStarted/Data/SampleDbContext.cs
+++ b/src/Examples/GettingStarted/Data/SampleDbContext.cs
@@ -5,7 +5,8 @@
namespace GettingStarted.Data;
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
-public class SampleDbContext(DbContextOptions options) : DbContext(options)
+public class SampleDbContext(DbContextOptions options)
+ : DbContext(options)
{
public DbSet Books => Set();
}
diff --git a/src/Examples/GettingStarted/GettingStarted.csproj b/src/Examples/GettingStarted/GettingStarted.csproj
index 1f4645f323..611aeb37a5 100644
--- a/src/Examples/GettingStarted/GettingStarted.csproj
+++ b/src/Examples/GettingStarted/GettingStarted.csproj
@@ -1,6 +1,6 @@
- net8.0;net6.0
+ net9.0;net8.0
diff --git a/src/Examples/GettingStarted/Program.cs b/src/Examples/GettingStarted/Program.cs
index 9ce6beda08..634e130a3f 100644
--- a/src/Examples/GettingStarted/Program.cs
+++ b/src/Examples/GettingStarted/Program.cs
@@ -38,7 +38,7 @@
await CreateDatabaseAsync(app.Services);
-app.Run();
+await app.RunAsync();
[Conditional("DEBUG")]
static void SetDbContextDebugOptions(DbContextOptionsBuilder options)
diff --git a/src/Examples/GettingStarted/README.md b/src/Examples/GettingStarted/README.md
index 563899a827..8d8da60bc8 100644
--- a/src/Examples/GettingStarted/README.md
+++ b/src/Examples/GettingStarted/README.md
@@ -11,4 +11,4 @@ For further documentation and implementation of a JsonApiDotNetCore Application
Repository: https://github.com/json-api-dotnet/JsonApiDotNetCore
-Documentation: http://www.jsonapi.net
+Documentation: https://www.jsonapi.net
diff --git a/src/Examples/JsonApiDotNetCoreExample/AppLog.cs b/src/Examples/JsonApiDotNetCoreExample/AppLog.cs
new file mode 100644
index 0000000000..6cb4af1a55
--- /dev/null
+++ b/src/Examples/JsonApiDotNetCoreExample/AppLog.cs
@@ -0,0 +1,9 @@
+#pragma warning disable AV1008 // Class should not be static
+
+namespace JsonApiDotNetCoreExample;
+
+internal static partial class AppLog
+{
+ [LoggerMessage(Level = LogLevel.Information, SkipEnabledCheck = true, Message = "Measurement results for application startup:{LineBreak}{TimingResults}")]
+ public static partial void LogStartupTimings(ILogger logger, string lineBreak, string timingResults);
+}
diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs
index 3c89ac3bcf..aa51110869 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/NonJsonApiController.cs
@@ -16,7 +16,8 @@ public IActionResult Get()
[HttpPost]
public async Task PostAsync()
{
- string name = await new StreamReader(Request.Body).ReadToEndAsync();
+ using var reader = new StreamReader(Request.Body, leaveOpen: true);
+ string name = await reader.ReadToEndAsync();
if (string.IsNullOrEmpty(name))
{
diff --git a/src/Examples/JsonApiDotNetCoreExample/Controllers/OperationsController.cs b/src/Examples/JsonApiDotNetCoreExample/Controllers/OperationsController.cs
index 9d8d944967..a5cb2ef2e3 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Controllers/OperationsController.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Controllers/OperationsController.cs
@@ -8,5 +8,5 @@ namespace JsonApiDotNetCoreExample.Controllers;
public sealed class OperationsController(
IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request,
- ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor,
- request, targetedFields, operationFilter);
+ ITargetedFields targetedFields, IAtomicOperationFilter operationFilter)
+ : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter);
diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
index e7864a42f6..f5c7e8e401 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
@@ -8,7 +8,8 @@
namespace JsonApiDotNetCoreExample.Data;
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
-public sealed class AppDbContext(DbContextOptions options) : DbContext(options)
+public sealed class AppDbContext(DbContextOptions options)
+ : DbContext(options)
{
public DbSet TodoItems => Set();
diff --git a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs
index d94fffa85b..06036968d0 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs
@@ -5,27 +5,14 @@
using JsonApiDotNetCore.Queries.Expressions;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCoreExample.Models;
-#if NET6_0
-using Microsoft.AspNetCore.Authentication;
-#endif
namespace JsonApiDotNetCoreExample.Definitions;
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
-public sealed class TodoItemDefinition(
- IResourceGraph resourceGraph,
-#if NET6_0
- ISystemClock systemClock
-#else
- TimeProvider timeProvider
-#endif
-) : JsonApiResourceDefinition(resourceGraph)
+public sealed class TodoItemDefinition(IResourceGraph resourceGraph, TimeProvider timeProvider)
+ : JsonApiResourceDefinition(resourceGraph)
{
-#if NET6_0
- private readonly Func _getUtcNow = () => systemClock.UtcNow;
-#else
- private readonly Func _getUtcNow = timeProvider.GetUtcNow;
-#endif
+ private readonly TimeProvider _timeProvider = timeProvider;
public override SortExpression OnApplySort(SortExpression? existingSort)
{
@@ -44,11 +31,11 @@ public override Task OnWritingAsync(TodoItem resource, WriteOperationKind writeO
{
if (writeOperation == WriteOperationKind.CreateResource)
{
- resource.CreatedAt = _getUtcNow();
+ resource.CreatedAt = _timeProvider.GetUtcNow();
}
else if (writeOperation == WriteOperationKind.UpdateResource)
{
- resource.LastModifiedAt = _getUtcNow();
+ resource.LastModifiedAt = _timeProvider.GetUtcNow();
}
return Task.CompletedTask;
diff --git a/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json
new file mode 100644
index 0000000000..4863000598
--- /dev/null
+++ b/src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json
@@ -0,0 +1,8583 @@
+{
+ "openapi": "3.0.4",
+ "info": {
+ "title": "JsonApiDotNetCoreExample",
+ "version": "1.0"
+ },
+ "servers": [
+ {
+ "url": "https://localhost:44340"
+ }
+ ],
+ "paths": {
+ "/api/operations": {
+ "post": {
+ "tags": [
+ "operations"
+ ],
+ "summary": "Performs multiple mutations in a linear and atomic manner.",
+ "operationId": "postOperations",
+ "requestBody": {
+ "description": "An array of mutation operations. For syntax, see the [Atomic Operations documentation](https://jsonapi.org/ext/atomic/).",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/operationsRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "All operations were successfully applied, which resulted in additional changes.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/operationsResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "All operations were successfully applied, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "An operation is not accessible or a client-generated ID is used.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A resource or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=atomic; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/people": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves a collection of people.",
+ "operationId": "getPersonCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found people, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/personCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves a collection of people without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPersonCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Creates a new person.",
+ "operationId": "postPerson",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the person to create.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/createPersonRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "201": {
+ "description": "The person was successfully created, which resulted in additional changes. The newly created person is returned.",
+ "headers": {
+ "Location": {
+ "description": "The URL at which the newly created person can be retrieved.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryPersonResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The person was successfully created, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Client-generated IDs cannot be used at this endpoint.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/people/{id}": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves an individual person by its identifier.",
+ "operationId": "getPerson",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found person.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryPersonResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves an individual person by its identifier without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPerson",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The person does not exist."
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Updates an existing person.",
+ "operationId": "patchPerson",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to update.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the person to update. Omitted fields are left unchanged.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updatePersonRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "The person was successfully updated, which resulted in additional changes. The updated person is returned.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryPersonResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The person was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "A resource type or identifier in the request body is incompatible.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Deletes an existing person by its identifier.",
+ "operationId": "deletePerson",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to delete.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "The person was successfully deleted."
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/people/{id}/assignedTodoItems": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItems of an individual person's assignedTodoItems relationship.",
+ "operationId": "getPersonAssignedTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItems, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItems of an individual person's assignedTodoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPersonAssignedTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The person does not exist."
+ }
+ }
+ }
+ },
+ "/api/people/{id}/relationships/assignedTodoItems": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual person's assignedTodoItems relationship.",
+ "operationId": "getPersonAssignedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItem identities, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual person's assignedTodoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPersonAssignedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The person does not exist."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Adds existing todoItems to the assignedTodoItems relationship of an individual person.",
+ "operationId": "postPersonAssignedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to add todoItems to.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to add to the assignedTodoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully added, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Assigns existing todoItems to the assignedTodoItems relationship of an individual person.",
+ "operationId": "patchPersonAssignedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose assignedTodoItems relationship to assign.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to assign to the assignedTodoItems relationship, or an empty array to clear the relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The assignedTodoItems relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Removes existing todoItems from the assignedTodoItems relationship of an individual person.",
+ "operationId": "deletePersonAssignedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to remove todoItems from.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to remove from the assignedTodoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully removed, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/people/{id}/ownedTodoItems": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItems of an individual person's ownedTodoItems relationship.",
+ "operationId": "getPersonOwnedTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItems, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItems of an individual person's ownedTodoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPersonOwnedTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The person does not exist."
+ }
+ }
+ }
+ },
+ "/api/people/{id}/relationships/ownedTodoItems": {
+ "get": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual person's ownedTodoItems relationship.",
+ "operationId": "getPersonOwnedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItem identities, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual person's ownedTodoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headPersonOwnedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The person does not exist."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Adds existing todoItems to the ownedTodoItems relationship of an individual person.",
+ "operationId": "postPersonOwnedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to add todoItems to.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to add to the ownedTodoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully added, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Assigns existing todoItems to the ownedTodoItems relationship of an individual person.",
+ "operationId": "patchPersonOwnedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person whose ownedTodoItems relationship to assign.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to assign to the ownedTodoItems relationship, or an empty array to clear the relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The ownedTodoItems relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "people"
+ ],
+ "summary": "Removes existing todoItems from the ownedTodoItems relationship of an individual person.",
+ "operationId": "deletePersonOwnedTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the person to remove todoItems from.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to remove from the ownedTodoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully removed, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The person or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/tags": {
+ "get": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves a collection of tags.",
+ "operationId": "getTagCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found tags, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/tagCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves a collection of tags without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTagCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Creates a new tag.",
+ "operationId": "postTag",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the tag to create.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/createTagRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "201": {
+ "description": "The tag was successfully created, which resulted in additional changes. The newly created tag is returned.",
+ "headers": {
+ "Location": {
+ "description": "The URL at which the newly created tag can be retrieved.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTagResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The tag was successfully created, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Client-generated IDs cannot be used at this endpoint.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/tags/{id}": {
+ "get": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves an individual tag by its identifier.",
+ "operationId": "getTag",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found tag.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTagResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves an individual tag by its identifier without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTag",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The tag does not exist."
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Updates an existing tag.",
+ "operationId": "patchTag",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to update.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the tag to update. Omitted fields are left unchanged.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateTagRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "The tag was successfully updated, which resulted in additional changes. The updated tag is returned.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTagResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The tag was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "A resource type or identifier in the request body is incompatible.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Deletes an existing tag by its identifier.",
+ "operationId": "deleteTag",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to delete.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "The tag was successfully deleted."
+ },
+ "404": {
+ "description": "The tag does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/tags/{id}/todoItems": {
+ "get": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves the related todoItems of an individual tag's todoItems relationship.",
+ "operationId": "getTagTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItems, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves the related todoItems of an individual tag's todoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTagTodoItems",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag whose related todoItems to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The tag does not exist."
+ }
+ }
+ }
+ },
+ "/api/tags/{id}/relationships/todoItems": {
+ "get": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual tag's todoItems relationship.",
+ "operationId": "getTagTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItem identities, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Retrieves the related todoItem identities of an individual tag's todoItems relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTagTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag whose related todoItem identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The tag does not exist."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Adds existing todoItems to the todoItems relationship of an individual tag.",
+ "operationId": "postTagTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to add todoItems to.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to add to the todoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully added, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Assigns existing todoItems to the todoItems relationship of an individual tag.",
+ "operationId": "patchTagTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag whose todoItems relationship to assign.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to assign to the todoItems relationship, or an empty array to clear the relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "tags"
+ ],
+ "summary": "Removes existing todoItems from the todoItems relationship of an individual tag.",
+ "operationId": "deleteTagTodoItemsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the tag to remove todoItems from.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the todoItems to remove from the todoItems relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The todoItems were successfully removed, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The tag or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/todoItems": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves a collection of todoItems.",
+ "operationId": "getTodoItemCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItems, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/todoItemCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves a collection of todoItems without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemCollection",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Creates a new todoItem.",
+ "operationId": "postTodoItem",
+ "parameters": [
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the todoItem to create.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/createTodoItemRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "201": {
+ "description": "The todoItem was successfully created, which resulted in additional changes. The newly created todoItem is returned.",
+ "headers": {
+ "Location": {
+ "description": "The URL at which the newly created todoItem can be retrieved.",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTodoItemResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The todoItem was successfully created, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "403": {
+ "description": "Client-generated IDs cannot be used at this endpoint.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "A related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves an individual todoItem by its identifier.",
+ "operationId": "getTodoItem",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found todoItem.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTodoItemResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves an individual todoItem by its identifier without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItem",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Updates an existing todoItem.",
+ "operationId": "patchTodoItem",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to update.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The attributes and relationships of the todoItem to update. Omitted fields are left unchanged.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateTodoItemRequestDocument"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "The todoItem was successfully updated, which resulted in additional changes. The updated todoItem is returned.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/primaryTodoItemResponseDocument"
+ }
+ }
+ }
+ },
+ "204": {
+ "description": "The todoItem was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The query string is invalid or the request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "A resource type or identifier in the request body is incompatible.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Deletes an existing todoItem by its identifier.",
+ "operationId": "deleteTodoItem",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to delete.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "The todoItem was successfully deleted."
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/assignee": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person of an individual todoItem's assignee relationship.",
+ "operationId": "getTodoItemAssignee",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found person, or `null` if it was not found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/nullableSecondaryPersonResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person of an individual todoItem's assignee relationship without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemAssignee",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/relationships/assignee": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person identity of an individual todoItem's assignee relationship.",
+ "operationId": "getTodoItemAssigneeRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person identity to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found person identity, or `null` if it was not found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/nullablePersonIdentifierResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person identity of an individual todoItem's assignee relationship without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemAssigneeRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person identity to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Clears or assigns an existing person to the assignee relationship of an individual todoItem.",
+ "operationId": "patchTodoItemAssigneeRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose assignee relationship to assign or clear.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identity of the person to assign to the assignee relationship, or `null` to clear the relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/nullableToOnePersonInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The assignee relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/owner": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person of an individual todoItem's owner relationship.",
+ "operationId": "getTodoItemOwner",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found person, or `null` if it was not found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/secondaryPersonResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person of an individual todoItem's owner relationship without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemOwner",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/relationships/owner": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person identity of an individual todoItem's owner relationship.",
+ "operationId": "getTodoItemOwnerRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person identity to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found person identity, or `null` if it was not found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/personIdentifierResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related person identity of an individual todoItem's owner relationship without returning it.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemOwnerRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related person identity to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Assigns an existing person to the owner relationship of an individual todoItem.",
+ "operationId": "patchTodoItemOwnerRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose owner relationship to assign.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identity of the person to assign to the owner relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toOnePersonInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The owner relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/tags": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related tags of an individual todoItem's tags relationship.",
+ "operationId": "getTodoItemTags",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related tags to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found tags, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/tagCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related tags of an individual todoItem's tags relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemTags",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related tags to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ }
+ },
+ "/api/todoItems/{id}/relationships/tags": {
+ "get": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related tag identities of an individual todoItem's tags relationship.",
+ "operationId": "getTodoItemTagsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related tag identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Successfully returns the found tag identities, or an empty array if none were found.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ },
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/tagIdentifierCollectionResponseDocument"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "head": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Retrieves the related tag identities of an individual todoItem's tags relationship without returning them.",
+ "description": "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.",
+ "operationId": "headTodoItemTagsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose related tag identities to retrieve.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ },
+ {
+ "name": "query",
+ "in": "query",
+ "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.",
+ "schema": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "nullable": true
+ },
+ "example": ""
+ }
+ },
+ {
+ "name": "If-None-Match",
+ "in": "header",
+ "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The operation completed successfully.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ "Content-Length": {
+ "description": "Size of the HTTP response body, in bytes.",
+ "required": true,
+ "schema": {
+ "type": "integer",
+ "format": "int64"
+ }
+ }
+ }
+ },
+ "304": {
+ "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.",
+ "headers": {
+ "ETag": {
+ "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "The query string is invalid."
+ },
+ "404": {
+ "description": "The todoItem does not exist."
+ }
+ }
+ },
+ "post": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Adds existing tags to the tags relationship of an individual todoItem.",
+ "operationId": "postTodoItemTagsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to add tags to.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the tags to add to the tags relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The tags were successfully added, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Assigns existing tags to the tags relationship of an individual todoItem.",
+ "operationId": "patchTodoItemTagsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem whose tags relationship to assign.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the tags to assign to the tags relationship, or an empty array to clear the relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The tags relationship was successfully updated, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ },
+ "delete": {
+ "tags": [
+ "todoItems"
+ ],
+ "summary": "Removes existing tags from the tags relationship of an individual todoItem.",
+ "operationId": "deleteTodoItemTagsRelationship",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "description": "The identifier of the todoItem to remove tags from.",
+ "required": true,
+ "schema": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ ],
+ "requestBody": {
+ "description": "The identities of the tags to remove from the tags relationship.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInRequest"
+ }
+ ]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "The tags were successfully removed, which did not result in additional changes."
+ },
+ "400": {
+ "description": "The request body is missing or malformed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "The todoItem or a related resource does not exist.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "409": {
+ "description": "The request body contains conflicting information or another resource with the same ID already exists.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ },
+ "422": {
+ "description": "Validation of the request body failed.",
+ "content": {
+ "application/vnd.api+json; ext=openapi": {
+ "schema": {
+ "$ref": "#/components/schemas/errorResponseDocument"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "addOperationCode": {
+ "enum": [
+ "add"
+ ],
+ "type": "string"
+ },
+ "addToPersonAssignedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personAssignedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "addToPersonOwnedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personOwnedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "addToTagTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "addToTodoItemTagsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemTagsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "atomicOperation": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "type": "string"
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "addPerson": "#/components/schemas/createPersonOperation",
+ "addTag": "#/components/schemas/createTagOperation",
+ "addToPersonAssignedTodoItems": "#/components/schemas/addToPersonAssignedTodoItemsRelationshipOperation",
+ "addToPersonOwnedTodoItems": "#/components/schemas/addToPersonOwnedTodoItemsRelationshipOperation",
+ "addToTagTodoItems": "#/components/schemas/addToTagTodoItemsRelationshipOperation",
+ "addToTodoItemTags": "#/components/schemas/addToTodoItemTagsRelationshipOperation",
+ "addTodoItem": "#/components/schemas/createTodoItemOperation",
+ "removeFromPersonAssignedTodoItems": "#/components/schemas/removeFromPersonAssignedTodoItemsRelationshipOperation",
+ "removeFromPersonOwnedTodoItems": "#/components/schemas/removeFromPersonOwnedTodoItemsRelationshipOperation",
+ "removeFromTagTodoItems": "#/components/schemas/removeFromTagTodoItemsRelationshipOperation",
+ "removeFromTodoItemTags": "#/components/schemas/removeFromTodoItemTagsRelationshipOperation",
+ "removePerson": "#/components/schemas/deletePersonOperation",
+ "removeTag": "#/components/schemas/deleteTagOperation",
+ "removeTodoItem": "#/components/schemas/deleteTodoItemOperation",
+ "updatePerson": "#/components/schemas/updatePersonOperation",
+ "updatePersonAssignedTodoItems": "#/components/schemas/updatePersonAssignedTodoItemsRelationshipOperation",
+ "updatePersonOwnedTodoItems": "#/components/schemas/updatePersonOwnedTodoItemsRelationshipOperation",
+ "updateTag": "#/components/schemas/updateTagOperation",
+ "updateTagTodoItems": "#/components/schemas/updateTagTodoItemsRelationshipOperation",
+ "updateTodoItem": "#/components/schemas/updateTodoItemOperation",
+ "updateTodoItemAssignee": "#/components/schemas/updateTodoItemAssigneeRelationshipOperation",
+ "updateTodoItemOwner": "#/components/schemas/updateTodoItemOwnerRelationshipOperation",
+ "updateTodoItemTags": "#/components/schemas/updateTodoItemTagsRelationshipOperation"
+ }
+ },
+ "x-abstract": true
+ },
+ "atomicResult": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "attributesInCreatePersonRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreateRequest"
+ },
+ {
+ "required": [
+ "lastName"
+ ],
+ "type": "object",
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "nullable": true
+ },
+ "lastName": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInCreateRequest": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/attributesInCreatePersonRequest",
+ "tags": "#/components/schemas/attributesInCreateTagRequest",
+ "todoItems": "#/components/schemas/attributesInCreateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "attributesInCreateTagRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreateRequest"
+ },
+ {
+ "required": [
+ "name"
+ ],
+ "type": "object",
+ "properties": {
+ "name": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInCreateTodoItemRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreateRequest"
+ },
+ {
+ "required": [
+ "description",
+ "priority"
+ ],
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "priority": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemPriority"
+ }
+ ]
+ },
+ "durationInHours": {
+ "type": "integer",
+ "format": "int64",
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInPersonResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "nullable": true
+ },
+ "lastName": {
+ "type": "string"
+ },
+ "displayName": {
+ "type": "string",
+ "readOnly": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInResponse": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/attributesInPersonResponse",
+ "tags": "#/components/schemas/attributesInTagResponse",
+ "todoItems": "#/components/schemas/attributesInTodoItemResponse"
+ }
+ },
+ "x-abstract": true
+ },
+ "attributesInTagResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInTodoItemResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "priority": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemPriority"
+ }
+ ]
+ },
+ "durationInHours": {
+ "type": "integer",
+ "format": "int64",
+ "nullable": true
+ },
+ "createdAt": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "modifiedAt": {
+ "type": "string",
+ "format": "date-time",
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInUpdatePersonRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "nullable": true
+ },
+ "lastName": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInUpdateRequest": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/attributesInUpdatePersonRequest",
+ "tags": "#/components/schemas/attributesInUpdateTagRequest",
+ "todoItems": "#/components/schemas/attributesInUpdateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "attributesInUpdateTagRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "name": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "attributesInUpdateTodoItemRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ },
+ "priority": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemPriority"
+ }
+ ]
+ },
+ "durationInHours": {
+ "type": "integer",
+ "format": "int64",
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "createPersonOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreatePersonRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "createPersonRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreatePersonRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "createTagOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreateTagRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "createTagRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreateTagRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "createTodoItemOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/addOperationCode"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreateTodoItemRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "createTodoItemRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInCreateTodoItemRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "dataInCreatePersonRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInCreateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreatePersonRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreatePersonRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInCreateTagRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInCreateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreateTagRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreateTagRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInCreateTodoItemRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInCreateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInCreateTodoItemRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreateTodoItemRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInPersonResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInResponse"
+ },
+ {
+ "required": [
+ "id"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInPersonResponse"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInPersonResponse"
+ }
+ ]
+ },
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceLinks"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInTagResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInResponse"
+ },
+ {
+ "required": [
+ "id"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInTagResponse"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInTagResponse"
+ }
+ ]
+ },
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceLinks"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInTodoItemResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInResponse"
+ },
+ {
+ "required": [
+ "id"
+ ],
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInTodoItemResponse"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInTodoItemResponse"
+ }
+ ]
+ },
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceLinks"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInUpdatePersonRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdatePersonRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdatePersonRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInUpdateTagRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdateTagRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdateTagRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "dataInUpdateTodoItemRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "attributes": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/attributesInUpdateTodoItemRequest"
+ }
+ ]
+ },
+ "relationships": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdateTodoItemRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "deletePersonOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "deleteTagOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "deleteTodoItemOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "errorLinks": {
+ "type": "object",
+ "properties": {
+ "about": {
+ "type": "string",
+ "nullable": true
+ },
+ "type": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ },
+ "errorObject": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "nullable": true
+ },
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/errorLinks"
+ }
+ ],
+ "nullable": true
+ },
+ "status": {
+ "type": "string"
+ },
+ "code": {
+ "type": "string",
+ "nullable": true
+ },
+ "title": {
+ "type": "string",
+ "nullable": true
+ },
+ "detail": {
+ "type": "string",
+ "nullable": true
+ },
+ "source": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/errorSource"
+ }
+ ],
+ "nullable": true
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "errorResponseDocument": {
+ "required": [
+ "errors",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/errorTopLevelLinks"
+ }
+ ]
+ },
+ "errors": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/errorObject"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "errorSource": {
+ "type": "object",
+ "properties": {
+ "pointer": {
+ "type": "string",
+ "nullable": true
+ },
+ "parameter": {
+ "type": "string",
+ "nullable": true
+ },
+ "header": {
+ "type": "string",
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ },
+ "errorTopLevelLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "describedby": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "identifierInRequest": {
+ "required": [
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "people": "#/components/schemas/personIdentifierInRequest",
+ "tags": "#/components/schemas/tagIdentifierInRequest",
+ "todoItems": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "meta": {
+ "type": "object",
+ "additionalProperties": {
+ "nullable": true
+ }
+ },
+ "nullablePersonIdentifierResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceIdentifierTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInResponse"
+ }
+ ],
+ "nullable": true
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "nullableSecondaryPersonResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInPersonResponse"
+ }
+ ],
+ "nullable": true
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "nullableToOnePersonInRequest": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ],
+ "nullable": true
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "nullableToOnePersonInResponse": {
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInResponse"
+ }
+ ],
+ "nullable": true
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "operationsRequestDocument": {
+ "required": [
+ "atomic:operations"
+ ],
+ "type": "object",
+ "properties": {
+ "atomic:operations": {
+ "minItems": 1,
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/atomicOperation"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "operationsResponseDocument": {
+ "required": [
+ "atomic:results",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "atomic:results": {
+ "minItems": 1,
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/atomicResult"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personAssignedTodoItemsRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personAssignedTodoItemsRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personAssignedTodoItemsRelationshipName": {
+ "enum": [
+ "assignedTodoItems"
+ ],
+ "type": "string"
+ },
+ "personCollectionResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceCollectionTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/dataInPersonResponse"
+ }
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personIdentifierInRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/identifierInRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "personIdentifierInResponse": {
+ "required": [
+ "id",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personIdentifierResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceIdentifierTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInResponse"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personOwnedTodoItemsRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personOwnedTodoItemsRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "personOwnedTodoItemsRelationshipName": {
+ "enum": [
+ "ownedTodoItems"
+ ],
+ "type": "string"
+ },
+ "personResourceType": {
+ "enum": [
+ "people"
+ ],
+ "type": "string"
+ },
+ "primaryPersonResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInPersonResponse"
+ }
+ ]
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "primaryTagResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInTagResponse"
+ }
+ ]
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "primaryTodoItemResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInTodoItemResponse"
+ }
+ ]
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "relationshipLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "related": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "relationshipsInCreatePersonRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "ownedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ },
+ "assignedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInCreateRequest": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/relationshipsInCreatePersonRequest",
+ "tags": "#/components/schemas/relationshipsInCreateTagRequest",
+ "todoItems": "#/components/schemas/relationshipsInCreateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "relationshipsInCreateTagRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "todoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInCreateTodoItemRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInCreateRequest"
+ },
+ {
+ "required": [
+ "owner"
+ ],
+ "type": "object",
+ "properties": {
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toOnePersonInRequest"
+ }
+ ]
+ },
+ "assignee": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/nullableToOnePersonInRequest"
+ }
+ ]
+ },
+ "tags": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInPersonResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "ownedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInResponse"
+ }
+ ]
+ },
+ "assignedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInResponse"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInResponse": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/relationshipsInPersonResponse",
+ "tags": "#/components/schemas/relationshipsInTagResponse",
+ "todoItems": "#/components/schemas/relationshipsInTodoItemResponse"
+ }
+ },
+ "x-abstract": true
+ },
+ "relationshipsInTagResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "todoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInResponse"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInTodoItemResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toOnePersonInResponse"
+ }
+ ]
+ },
+ "assignee": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/nullableToOnePersonInResponse"
+ }
+ ]
+ },
+ "tags": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInResponse"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInUpdatePersonRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "ownedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ },
+ "assignedTodoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInUpdateRequest": {
+ "required": [
+ "openapi:discriminator"
+ ],
+ "type": "object",
+ "properties": {
+ "openapi:discriminator": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "openapi:discriminator",
+ "mapping": {
+ "people": "#/components/schemas/relationshipsInUpdatePersonRequest",
+ "tags": "#/components/schemas/relationshipsInUpdateTagRequest",
+ "todoItems": "#/components/schemas/relationshipsInUpdateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "relationshipsInUpdateTagRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "todoItems": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTodoItemInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "relationshipsInUpdateTodoItemRequest": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipsInUpdateRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "owner": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toOnePersonInRequest"
+ }
+ ]
+ },
+ "assignee": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/nullableToOnePersonInRequest"
+ }
+ ]
+ },
+ "tags": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/toManyTagInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "removeFromPersonAssignedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personAssignedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "removeFromPersonOwnedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personOwnedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "removeFromTagTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "removeFromTodoItemTagsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/removeOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemTagsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "removeOperationCode": {
+ "enum": [
+ "remove"
+ ],
+ "type": "string"
+ },
+ "resourceCollectionTopLevelLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "describedby": {
+ "type": "string"
+ },
+ "first": {
+ "type": "string"
+ },
+ "last": {
+ "type": "string"
+ },
+ "prev": {
+ "type": "string"
+ },
+ "next": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resourceIdentifierCollectionTopLevelLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "related": {
+ "type": "string"
+ },
+ "describedby": {
+ "type": "string"
+ },
+ "first": {
+ "type": "string"
+ },
+ "last": {
+ "type": "string"
+ },
+ "prev": {
+ "type": "string"
+ },
+ "next": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resourceIdentifierTopLevelLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "related": {
+ "type": "string"
+ },
+ "describedby": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resourceInCreateRequest": {
+ "required": [
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "people": "#/components/schemas/dataInCreatePersonRequest",
+ "tags": "#/components/schemas/dataInCreateTagRequest",
+ "todoItems": "#/components/schemas/dataInCreateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "resourceInResponse": {
+ "required": [
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "people": "#/components/schemas/dataInPersonResponse",
+ "tags": "#/components/schemas/dataInTagResponse",
+ "todoItems": "#/components/schemas/dataInTodoItemResponse"
+ }
+ },
+ "x-abstract": true
+ },
+ "resourceInUpdateRequest": {
+ "required": [
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceType"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "discriminator": {
+ "propertyName": "type",
+ "mapping": {
+ "people": "#/components/schemas/dataInUpdatePersonRequest",
+ "tags": "#/components/schemas/dataInUpdateTagRequest",
+ "todoItems": "#/components/schemas/dataInUpdateTodoItemRequest"
+ }
+ },
+ "x-abstract": true
+ },
+ "resourceLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resourceTopLevelLinks": {
+ "type": "object",
+ "properties": {
+ "self": {
+ "type": "string"
+ },
+ "describedby": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resourceType": {
+ "enum": [
+ "people",
+ "tags",
+ "todoItems"
+ ],
+ "type": "string"
+ },
+ "secondaryPersonResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInPersonResponse"
+ }
+ ]
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "tagCollectionResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceCollectionTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/dataInTagResponse"
+ }
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "tagIdentifierCollectionResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceIdentifierCollectionTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "tagIdentifierInRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/identifierInRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "tagIdentifierInResponse": {
+ "required": [
+ "id",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "tagResourceType": {
+ "enum": [
+ "tags"
+ ],
+ "type": "string"
+ },
+ "tagTodoItemsRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagTodoItemsRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "tagTodoItemsRelationshipName": {
+ "enum": [
+ "todoItems"
+ ],
+ "type": "string"
+ },
+ "toManyTagInRequest": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "toManyTagInResponse": {
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "toManyTodoItemInRequest": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "toManyTodoItemInResponse": {
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "toOnePersonInRequest": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "toOnePersonInResponse": {
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/relationshipLinks"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInResponse"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemAssigneeRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemAssigneeRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemAssigneeRelationshipName": {
+ "enum": [
+ "assignee"
+ ],
+ "type": "string"
+ },
+ "todoItemCollectionResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceCollectionTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/dataInTodoItemResponse"
+ }
+ },
+ "included": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/resourceInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemIdentifierCollectionResponseDocument": {
+ "required": [
+ "data",
+ "links"
+ ],
+ "type": "object",
+ "properties": {
+ "links": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/resourceIdentifierCollectionTopLevelLinks"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInResponse"
+ }
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemIdentifierInRequest": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/identifierInRequest"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "todoItemIdentifierInResponse": {
+ "required": [
+ "id",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemOwnerRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemOwnerRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemOwnerRelationshipName": {
+ "enum": [
+ "owner"
+ ],
+ "type": "string"
+ },
+ "todoItemPriority": {
+ "enum": [
+ "High",
+ "Medium",
+ "Low"
+ ],
+ "type": "string"
+ },
+ "todoItemResourceType": {
+ "enum": [
+ "todoItems"
+ ],
+ "type": "string"
+ },
+ "todoItemTagsRelationshipIdentifier": {
+ "required": [
+ "relationship",
+ "type"
+ ],
+ "type": "object",
+ "properties": {
+ "type": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemResourceType"
+ }
+ ]
+ },
+ "id": {
+ "minLength": 1,
+ "type": "string",
+ "format": "int64"
+ },
+ "lid": {
+ "minLength": 1,
+ "type": "string"
+ },
+ "relationship": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemTagsRelationshipName"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "todoItemTagsRelationshipName": {
+ "enum": [
+ "tags"
+ ],
+ "type": "string"
+ },
+ "updateOperationCode": {
+ "enum": [
+ "update"
+ ],
+ "type": "string"
+ },
+ "updatePersonAssignedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personAssignedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updatePersonOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdatePersonRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updatePersonOwnedTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personOwnedTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updatePersonRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdatePersonRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "updateTagOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdateTagRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updateTagRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdateTagRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "updateTagTodoItemsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/tagTodoItemsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updateTodoItemAssigneeRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemAssigneeRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ],
+ "nullable": true
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updateTodoItemOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemIdentifierInRequest"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdateTodoItemRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updateTodoItemOwnerRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemOwnerRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/personIdentifierInRequest"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ },
+ "updateTodoItemRequestDocument": {
+ "required": [
+ "data"
+ ],
+ "type": "object",
+ "properties": {
+ "data": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/dataInUpdateTodoItemRequest"
+ }
+ ]
+ },
+ "meta": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/meta"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "updateTodoItemTagsRelationshipOperation": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/atomicOperation"
+ },
+ {
+ "required": [
+ "data",
+ "op",
+ "ref"
+ ],
+ "type": "object",
+ "properties": {
+ "op": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/updateOperationCode"
+ }
+ ]
+ },
+ "ref": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/todoItemTagsRelationshipIdentifier"
+ }
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/tagIdentifierInRequest"
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ ],
+ "additionalProperties": false
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
index 0ccb4bbc5f..768a2de827 100644
--- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
+++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj
@@ -1,6 +1,8 @@
- net8.0;net6.0
+ net9.0;net8.0
+ true
+ GeneratedSwagger
@@ -9,10 +11,15 @@
+
+
+
+
+
diff --git a/src/Examples/JsonApiDotNetCoreExample/Program.cs b/src/Examples/JsonApiDotNetCoreExample/Program.cs
index 52b27759e9..d2677ea781 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Program.cs
+++ b/src/Examples/JsonApiDotNetCoreExample/Program.cs
@@ -3,21 +3,24 @@
using System.Text.Json.Serialization;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Diagnostics;
+using JsonApiDotNetCore.OpenApi.Swashbuckle;
+using JsonApiDotNetCoreExample;
using JsonApiDotNetCoreExample.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection.Extensions;
-#if NET6_0
-using Microsoft.AspNetCore.Authentication;
-#endif
+using Scalar.AspNetCore;
[assembly: ExcludeFromCodeCoverage]
WebApplication app = CreateWebApplication(args);
-await CreateDatabaseAsync(app.Services);
+if (!IsGeneratingOpenApiDocumentAtBuildTime())
+{
+ await CreateDatabaseAsync(app.Services);
+}
-app.Run();
+await app.RunAsync();
static WebApplication CreateWebApplication(string[] args)
{
@@ -34,10 +37,10 @@ static WebApplication CreateWebApplication(string[] args)
// Configure the HTTP request pipeline.
ConfigurePipeline(app);
- if (CodeTimingSessionManager.IsEnabled)
+ if (CodeTimingSessionManager.IsEnabled && app.Logger.IsEnabled(LogLevel.Information))
{
string timingResults = CodeTimingSessionManager.Current.GetResults();
- app.Logger.LogInformation($"Measurement results for application startup:{Environment.NewLine}{timingResults}");
+ AppLog.LogStartupTimings(app.Logger, Environment.NewLine, timingResults);
}
return app;
@@ -47,11 +50,7 @@ static void ConfigureServices(WebApplicationBuilder builder)
{
using IDisposable _ = CodeTimingSessionManager.Current.Measure("Configure services");
-#if NET6_0
- builder.Services.TryAddSingleton();
-#else
builder.Services.TryAddSingleton(TimeProvider.System);
-#endif
builder.Services.AddDbContext(options =>
{
@@ -77,6 +76,13 @@ static void ConfigureServices(WebApplicationBuilder builder)
#endif
}, discovery => discovery.AddCurrentAssembly());
}
+
+ using (CodeTimingSessionManager.Current.Measure("AddOpenApiForJsonApi()"))
+ {
+#pragma warning disable JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ builder.Services.AddOpenApiForJsonApi(options => options.DocumentFilter());
+#pragma warning restore JADNC_OA_001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ }
}
[Conditional("DEBUG")]
@@ -98,9 +104,19 @@ static void ConfigurePipeline(WebApplication app)
app.UseJsonApi();
}
+ app.UseSwagger();
+ app.UseSwaggerUI();
+ app.UseReDoc();
+ app.MapScalarApiReference(options => options.OpenApiRoutePattern = "/swagger/{documentName}/swagger.json");
+
app.MapControllers();
}
+static bool IsGeneratingOpenApiDocumentAtBuildTime()
+{
+ return Environment.GetCommandLineArgs().Any(argument => argument.Contains("GetDocument.Insider"));
+}
+
static async Task CreateDatabaseAsync(IServiceProvider serviceProvider)
{
await using AsyncServiceScope scope = serviceProvider.CreateAsyncScope();
diff --git a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
index 54646922e1..82b89e1843 100644
--- a/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
+++ b/src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json
@@ -12,7 +12,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
- "launchUrl": "api/todoItems?include=owner,assignee,tags&filter=equals(priority,'High')",
+ "launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -20,7 +20,7 @@
"Kestrel": {
"commandName": "Project",
"launchBrowser": true,
- "launchUrl": "api/todoItems?include=owner,assignee,tags&filter=equals(priority,'High')",
+ "launchUrl": "swagger",
"applicationUrl": "https://localhost:44340;http://localhost:14140",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
diff --git a/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs b/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs
new file mode 100644
index 0000000000..894c0d0966
--- /dev/null
+++ b/src/Examples/JsonApiDotNetCoreExample/SetOpenApiServerAtBuildTimeFilter.cs
@@ -0,0 +1,25 @@
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Swashbuckle.AspNetCore.SwaggerGen;
+
+namespace JsonApiDotNetCoreExample;
+
+///
+/// This is normally not needed. It ensures the server URL is added to the OpenAPI file during build.
+///
+[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
+internal sealed class SetOpenApiServerAtBuildTimeFilter(IHttpContextAccessor httpContextAccessor) : IDocumentFilter
+{
+ private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
+
+ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
+ {
+ if (_httpContextAccessor.HttpContext == null)
+ {
+ swaggerDoc.Servers.Add(new OpenApiServer
+ {
+ Url = "https://localhost:44340"
+ });
+ }
+ }
+}
diff --git a/src/Examples/MultiDbContextExample/Data/DbContextA.cs b/src/Examples/MultiDbContextExample/Data/DbContextA.cs
index 4efd10ea7b..32f6197600 100644
--- a/src/Examples/MultiDbContextExample/Data/DbContextA.cs
+++ b/src/Examples/MultiDbContextExample/Data/DbContextA.cs
@@ -5,7 +5,8 @@
namespace MultiDbContextExample.Data;
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
-public sealed class DbContextA(DbContextOptions options) : DbContext(options)
+public sealed class DbContextA(DbContextOptions options)
+ : DbContext(options)
{
public DbSet ResourceAs => Set();
}
diff --git a/src/Examples/MultiDbContextExample/Data/DbContextB.cs b/src/Examples/MultiDbContextExample/Data/DbContextB.cs
index faf50c0902..8759e28e91 100644
--- a/src/Examples/MultiDbContextExample/Data/DbContextB.cs
+++ b/src/Examples/MultiDbContextExample/Data/DbContextB.cs
@@ -5,7 +5,8 @@
namespace MultiDbContextExample.Data;
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
-public sealed class DbContextB(DbContextOptions options) : DbContext(options)
+public sealed class DbContextB(DbContextOptions options)
+ : DbContext(options)
{
public DbSet ResourceBs => Set();
}
diff --git a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
index 1f4645f323..611aeb37a5 100644
--- a/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
+++ b/src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
@@ -1,6 +1,6 @@
- net8.0;net6.0
+ net9.0;net8.0
diff --git a/src/Examples/MultiDbContextExample/Program.cs b/src/Examples/MultiDbContextExample/Program.cs
index 2cf567b9b5..481e8f7118 100644
--- a/src/Examples/MultiDbContextExample/Program.cs
+++ b/src/Examples/MultiDbContextExample/Program.cs
@@ -52,7 +52,7 @@
await CreateDatabaseAsync(app.Services);
-app.Run();
+await app.RunAsync();
[Conditional("DEBUG")]
static void SetDbContextDebugOptions(DbContextOptionsBuilder options)
diff --git a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
index c5b18320f0..15a485c08f 100644
--- a/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
+++ b/src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj
@@ -1,6 +1,6 @@
- net8.0;net6.0
+ net9.0;net8.0
diff --git a/src/Examples/NoEntityFrameworkExample/Program.cs b/src/Examples/NoEntityFrameworkExample/Program.cs
index f21d116e5f..8eff35d7a9 100755
--- a/src/Examples/NoEntityFrameworkExample/Program.cs
+++ b/src/Examples/NoEntityFrameworkExample/Program.cs
@@ -35,4 +35,4 @@
app.UseJsonApi();
app.MapControllers();
-app.Run();
+await app.RunAsync();
diff --git a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs
index 9d0852ad7f..4feb370858 100644
--- a/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs
+++ b/src/Examples/NoEntityFrameworkExample/Repositories/InMemoryResourceRepository.cs
@@ -32,7 +32,7 @@ public Task> GetAsync(QueryLayer queryLayer, Canc
IEnumerable dataSource = GetDataSource();
IEnumerable resources = _queryLayerToLinqConverter.ApplyQueryLayer(queryLayer, dataSource);
- return Task.FromResult>(resources.ToList());
+ return Task.FromResult>(resources.ToArray().AsReadOnly());
}
///
diff --git a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs
index ee9d2196e9..0dcc5d9905 100644
--- a/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs
+++ b/src/Examples/NoEntityFrameworkExample/Services/InMemoryResourceService.cs
@@ -31,7 +31,7 @@ namespace NoEntityFrameworkExample.Services;
///
/// The resource identifier type.
///
-public abstract class InMemoryResourceService(
+public abstract partial class InMemoryResourceService(
IJsonApiOptions options, IResourceGraph resourceGraph, IQueryLayerComposer queryLayerComposer, IPaginationContext paginationContext,
IEnumerable constraintProviders, IQueryableBuilder queryableBuilder, IReadOnlyModel entityModel,
ILoggerFactory loggerFactory) : IResourceQueryService
@@ -40,7 +40,7 @@ public abstract class InMemoryResourceService(
private readonly IJsonApiOptions _options = options;
private readonly IQueryLayerComposer _queryLayerComposer = queryLayerComposer;
private readonly IPaginationContext _paginationContext = paginationContext;
- private readonly IEnumerable _constraintProviders = constraintProviders;
+ private readonly IQueryConstraintProvider[] _constraintProviders = constraintProviders as IQueryConstraintProvider[] ?? constraintProviders.ToArray();
private readonly ILogger> _logger = loggerFactory.CreateLogger>();
private readonly ResourceType _resourceType = resourceGraph.GetResourceType();
private readonly QueryLayerToLinqConverter _queryLayerToLinqConverter = new(entityModel, queryableBuilder);
@@ -58,14 +58,14 @@ public Task> GetAsync(CancellationToken cancellat
QueryLayer queryLayer = _queryLayerComposer.ComposeFromConstraints(_resourceType);
IEnumerable dataSource = GetDataSource(_resourceType).Cast();
- List resources = _queryLayerToLinqConverter.ApplyQueryLayer(queryLayer, dataSource).ToList();
+ TResource[] resources = _queryLayerToLinqConverter.ApplyQueryLayer(queryLayer, dataSource).ToArray();
- if (queryLayer.Pagination?.PageSize?.Value == resources.Count)
+ if (queryLayer.Pagination?.PageSize?.Value == resources.Length)
{
_paginationContext.IsPageFull = true;
}
- return Task.FromResult>(resources);
+ return Task.FromResult>(resources.AsReadOnly());
}
private void LogFiltersInTopScope()
@@ -87,7 +87,7 @@ private void LogFiltersInTopScope()
if (filter != null)
{
- _logger.LogInformation($"Incoming top-level filter from query string: {filter}");
+ LogIncomingFilter(filter);
}
}
@@ -195,4 +195,7 @@ private void SetNonPrimaryTotalCount([DisallowNull] TId id, RelationshipAttribut
}
protected abstract IEnumerable GetDataSource(ResourceType resourceType);
+
+ [LoggerMessage(Level = LogLevel.Information, Message = "Incoming top-level filter from query string: {Filter}")]
+ private partial void LogIncomingFilter(FilterExpression filter);
}
diff --git a/src/Examples/OpenApiKiotaClientExample/ColoredConsoleLogHttpMessageHandler.cs b/src/Examples/OpenApiKiotaClientExample/ColoredConsoleLogHttpMessageHandler.cs
new file mode 100644
index 0000000000..e13aff5a4d
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/ColoredConsoleLogHttpMessageHandler.cs
@@ -0,0 +1,67 @@
+using JetBrains.Annotations;
+
+namespace OpenApiKiotaClientExample;
+
+///
+/// Writes incoming and outgoing HTTP messages to the console.
+///
+internal sealed class ColoredConsoleLogHttpMessageHandler : DelegatingHandler
+{
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+#if DEBUG
+ await LogRequestAsync(request, cancellationToken);
+#endif
+
+ HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
+
+#if DEBUG
+ await LogResponseAsync(response, cancellationToken);
+#endif
+
+ return response;
+ }
+
+ [UsedImplicitly]
+ private static async Task LogRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ using var _ = new ConsoleColorScope(ConsoleColor.Green);
+
+ Console.WriteLine($"--> {request}");
+ string? requestBody = request.Content != null ? await request.Content.ReadAsStringAsync(cancellationToken) : null;
+
+ if (!string.IsNullOrEmpty(requestBody))
+ {
+ Console.WriteLine();
+ Console.WriteLine(requestBody);
+ }
+ }
+
+ [UsedImplicitly]
+ private static async Task LogResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken)
+ {
+ using var _ = new ConsoleColorScope(ConsoleColor.Cyan);
+
+ Console.WriteLine($"<-- {response}");
+ string responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
+
+ if (!string.IsNullOrEmpty(responseBody))
+ {
+ Console.WriteLine();
+ Console.WriteLine(responseBody);
+ }
+ }
+
+ private sealed class ConsoleColorScope : IDisposable
+ {
+ public ConsoleColorScope(ConsoleColor foregroundColor)
+ {
+ Console.ForegroundColor = foregroundColor;
+ }
+
+ public void Dispose()
+ {
+ Console.ResetColor();
+ }
+ }
+}
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/ApiRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/ApiRequestBuilder.cs
new file mode 100644
index 0000000000..3d0f7361e7
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/ApiRequestBuilder.cs
@@ -0,0 +1,66 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Api.Operations;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People;
+using OpenApiKiotaClientExample.GeneratedCode.Api.Tags;
+using OpenApiKiotaClientExample.GeneratedCode.Api.TodoItems;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api
+{
+ ///
+ /// Builds and executes requests for operations under \api
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class ApiRequestBuilder : BaseRequestBuilder
+ {
+ /// The operations property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.Operations.OperationsRequestBuilder Operations
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.Operations.OperationsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The people property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.PeopleRequestBuilder People
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.PeopleRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The tags property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.Tags.TagsRequestBuilder Tags
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.Tags.TagsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The todoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.TodoItems.TodoItemsRequestBuilder TodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.TodoItems.TodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public ApiRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public ApiRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api", rawUrl)
+ {
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Operations/OperationsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Operations/OperationsRequestBuilder.cs
new file mode 100644
index 0000000000..96f21c5a24
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Operations/OperationsRequestBuilder.cs
@@ -0,0 +1,94 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.Operations
+{
+ ///
+ /// Builds and executes requests for operations under \api\operations
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OperationsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public OperationsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/operations", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public OperationsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/operations", rawUrl)
+ {
+ }
+
+ ///
+ /// Performs multiple mutations in a linear and atomic manner.
+ ///
+ /// A
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 403 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PostAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.OperationsRequestDocument body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPostRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "403", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.OperationsResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Performs multiple mutations in a linear and atomic manner.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPostRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.OperationsRequestDocument body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=atomic;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=atomic;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.Operations.OperationsRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.Operations.OperationsRequestBuilder(rawUrl, RequestAdapter);
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs
new file mode 100644
index 0000000000..7bcc782af9
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs
@@ -0,0 +1,128 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}\assignedTodoItems
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public AssignedTodoItemsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/assignedTodoItems{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public AssignedTodoItemsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/assignedTodoItems{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's assignedTodoItems relationship.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.TodoItemCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's assignedTodoItems relationship.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems.AssignedTodoItemsRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems.AssignedTodoItemsRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's assignedTodoItems relationship.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs
new file mode 100644
index 0000000000..2981e5fa65
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs
@@ -0,0 +1,128 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}\ownedTodoItems
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public OwnedTodoItemsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/ownedTodoItems{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public OwnedTodoItemsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/ownedTodoItems{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's ownedTodoItems relationship.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.TodoItemCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's ownedTodoItems relationship.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems.OwnedTodoItemsRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems.OwnedTodoItemsRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves the related todoItems of an individual person's ownedTodoItems relationship.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/PeopleItemRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/PeopleItemRequestBuilder.cs
new file mode 100644
index 0000000000..567ed904c8
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/PeopleItemRequestBuilder.cs
@@ -0,0 +1,230 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleItemRequestBuilder : BaseRequestBuilder
+ {
+ /// The assignedTodoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems.AssignedTodoItemsRequestBuilder AssignedTodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems.AssignedTodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The ownedTodoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems.OwnedTodoItemsRequestBuilder OwnedTodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.OwnedTodoItems.OwnedTodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The relationships property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.RelationshipsRequestBuilder Relationships
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.RelationshipsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public PeopleItemRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public PeopleItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Deletes an existing person by its identifier.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 404 status code
+ public async Task DeleteAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToDeleteRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves an individual person by its identifier.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.PrimaryPersonResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Updates an existing person.
+ ///
+ /// A
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PatchAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonRequestDocument body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPatchRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.PrimaryPersonResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Deletes an existing person by its identifier.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToDeleteRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Retrieves an individual person by its identifier.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Updates an existing person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPatchRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonRequestDocument body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.PATCH, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.PeopleItemRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.PeopleItemRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves an individual person by its identifier.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleItemRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleItemRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Updates an existing person.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleItemRequestBuilderPatchQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs
new file mode 100644
index 0000000000..14c018d51f
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/AssignedTodoItems/AssignedTodoItemsRequestBuilder.cs
@@ -0,0 +1,248 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}\relationships\assignedTodoItems
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public AssignedTodoItemsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships/assignedTodoItems{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public AssignedTodoItemsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships/assignedTodoItems{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Removes existing todoItems from the assignedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task DeleteAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToDeleteRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's assignedTodoItems relationship.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.TodoItemIdentifierCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Assigns existing todoItems to the assignedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PatchAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPatchRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Adds existing todoItems to the assignedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PostAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPostRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Removes existing todoItems from the assignedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToDeleteRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's assignedTodoItems relationship.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Assigns existing todoItems to the assignedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPatchRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.PATCH, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Adds existing todoItems to the assignedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPostRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems.AssignedTodoItemsRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems.AssignedTodoItemsRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's assignedTodoItems relationship.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class AssignedTodoItemsRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs
new file mode 100644
index 0000000000..9f27d97c3b
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/OwnedTodoItems/OwnedTodoItemsRequestBuilder.cs
@@ -0,0 +1,248 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}\relationships\ownedTodoItems
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public OwnedTodoItemsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships/ownedTodoItems{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public OwnedTodoItemsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships/ownedTodoItems{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Removes existing todoItems from the ownedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task DeleteAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToDeleteRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's ownedTodoItems relationship.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.TodoItemIdentifierCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Assigns existing todoItems to the ownedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PatchAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPatchRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Adds existing todoItems to the ownedTodoItems relationship of an individual person.
+ ///
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PostAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPostRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Removes existing todoItems from the ownedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToDeleteRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's ownedTodoItems relationship.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Assigns existing todoItems to the ownedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPatchRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.PATCH, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Adds existing todoItems to the ownedTodoItems relationship of an individual person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPostRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.ToManyTodoItemInRequest body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems.OwnedTodoItemsRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems.OwnedTodoItemsRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves the related todoItem identities of an individual person's ownedTodoItems relationship.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class OwnedTodoItemsRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/RelationshipsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/RelationshipsRequestBuilder.cs
new file mode 100644
index 0000000000..2c9e9beafe
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/Item/Relationships/RelationshipsRequestBuilder.cs
@@ -0,0 +1,52 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships
+{
+ ///
+ /// Builds and executes requests for operations under \api\people\{id}\relationships
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class RelationshipsRequestBuilder : BaseRequestBuilder
+ {
+ /// The assignedTodoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems.AssignedTodoItemsRequestBuilder AssignedTodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.AssignedTodoItems.AssignedTodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ /// The ownedTodoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems.OwnedTodoItemsRequestBuilder OwnedTodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.Relationships.OwnedTodoItems.OwnedTodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public RelationshipsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public RelationshipsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/relationships", rawUrl)
+ {
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/PeopleRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/PeopleRequestBuilder.cs
new file mode 100644
index 0000000000..3a36d323c4
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/People/PeopleRequestBuilder.cs
@@ -0,0 +1,194 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Api.People.Item;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.People
+{
+ ///
+ /// Builds and executes requests for operations under \api\people
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleRequestBuilder : BaseRequestBuilder
+ {
+ /// Gets an item from the OpenApiKiotaClientExample.GeneratedCode.api.people.item collection
+ /// The identifier of the person to retrieve.
+ /// A
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.PeopleItemRequestBuilder this[string position]
+ {
+ get
+ {
+ var urlTplParams = new Dictionary(PathParameters);
+ urlTplParams.Add("id", position);
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.PeopleItemRequestBuilder(urlTplParams, RequestAdapter);
+ }
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public PeopleRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public PeopleRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people{?query*}", rawUrl)
+ {
+ }
+
+ ///
+ /// Retrieves a collection of people.
+ ///
+ /// A
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ public async Task GetAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToGetRequestInformation(requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.PersonCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public async Task HeadAsync(Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ var requestInfo = ToHeadRequestInformation(requestConfiguration);
+ await RequestAdapter.SendNoContentAsync(requestInfo, default, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Creates a new person.
+ ///
+ /// A
+ /// The request body
+ /// Cancellation token to use when cancelling requests
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ /// When receiving a 400 status code
+ /// When receiving a 403 status code
+ /// When receiving a 404 status code
+ /// When receiving a 409 status code
+ /// When receiving a 422 status code
+ public async Task PostAsync(global::OpenApiKiotaClientExample.GeneratedCode.Models.CreatePersonRequestDocument body, Action>? requestConfiguration = default, CancellationToken cancellationToken = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = ToPostRequestInformation(body, requestConfiguration);
+ var errorMapping = new Dictionary>
+ {
+ { "400", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "403", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "404", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "409", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ { "422", global::OpenApiKiotaClientExample.GeneratedCode.Models.ErrorResponseDocument.CreateFromDiscriminatorValue },
+ };
+ return await RequestAdapter.SendAsync(requestInfo, global::OpenApiKiotaClientExample.GeneratedCode.Models.PrimaryPersonResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Retrieves a collection of people.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToGetRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ return requestInfo;
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ /// A
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToHeadRequestInformation(Action>? requestConfiguration = default)
+ {
+ var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ return requestInfo;
+ }
+
+ ///
+ /// Creates a new person.
+ ///
+ /// A
+ /// The request body
+ /// Configuration for the request such as headers, query parameters, and middleware options.
+ public RequestInformation ToPostRequestInformation(global::OpenApiKiotaClientExample.GeneratedCode.Models.CreatePersonRequestDocument body, Action>? requestConfiguration = default)
+ {
+ _ = body ?? throw new ArgumentNullException(nameof(body));
+ var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
+ requestInfo.Configure(requestConfiguration);
+ requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json;ext=openapi");
+ requestInfo.SetContentFromParsable(RequestAdapter, "application/vnd.api+json;ext=openapi", body);
+ return requestInfo;
+ }
+
+ ///
+ /// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
+ ///
+ /// A
+ /// The raw URL to use for the request builder.
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.People.PeopleRequestBuilder WithUrl(string rawUrl)
+ {
+ return new global::OpenApiKiotaClientExample.GeneratedCode.Api.People.PeopleRequestBuilder(rawUrl, RequestAdapter);
+ }
+
+ ///
+ /// Retrieves a collection of people.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleRequestBuilderGetQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleRequestBuilderHeadQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+
+ ///
+ /// Creates a new person.
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class PeopleRequestBuilderPostQueryParameters
+ {
+ /// For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.
+ [QueryParameter("query")]
+ public string? Query { get; set; }
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/RelationshipsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/RelationshipsRequestBuilder.cs
new file mode 100644
index 0000000000..ecf2f5cc86
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/RelationshipsRequestBuilder.cs
@@ -0,0 +1,45 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Api.Tags.Item.Relationships.TodoItems;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.Tags.Item.Relationships
+{
+ ///
+ /// Builds and executes requests for operations under \api\tags\{id}\relationships
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class RelationshipsRequestBuilder : BaseRequestBuilder
+ {
+ /// The todoItems property
+ public global::OpenApiKiotaClientExample.GeneratedCode.Api.Tags.Item.Relationships.TodoItems.TodoItemsRequestBuilder TodoItems
+ {
+ get => new global::OpenApiKiotaClientExample.GeneratedCode.Api.Tags.Item.Relationships.TodoItems.TodoItemsRequestBuilder(PathParameters, RequestAdapter);
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public RelationshipsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/tags/{id}/relationships", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// The raw URL to use for the request builder.
+ /// The request adapter to use to execute the requests.
+ public RelationshipsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/tags/{id}/relationships", rawUrl)
+ {
+ }
+ }
+}
+#pragma warning restore CS0618
diff --git a/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/TodoItems/TodoItemsRequestBuilder.cs b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/TodoItems/TodoItemsRequestBuilder.cs
new file mode 100644
index 0000000000..6020933f3d
--- /dev/null
+++ b/src/Examples/OpenApiKiotaClientExample/GeneratedCode/Api/Tags/Item/Relationships/TodoItems/TodoItemsRequestBuilder.cs
@@ -0,0 +1,248 @@
+//
+#nullable enable
+#pragma warning disable CS8625
+#pragma warning disable CS0618
+using Microsoft.Kiota.Abstractions.Extensions;
+using Microsoft.Kiota.Abstractions.Serialization;
+using Microsoft.Kiota.Abstractions;
+using OpenApiKiotaClientExample.GeneratedCode.Models;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+namespace OpenApiKiotaClientExample.GeneratedCode.Api.Tags.Item.Relationships.TodoItems
+{
+ ///
+ /// Builds and executes requests for operations under \api\tags\{id}\relationships\todoItems
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
+ public partial class TodoItemsRequestBuilder : BaseRequestBuilder
+ {
+ ///
+ /// Instantiates a new and sets the default values.
+ ///
+ /// Path parameters for the request
+ /// The request adapter to use to execute the requests.
+ public TodoItemsRequestBuilder(Dictionary pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/tags/{id}/relationships/todoItems{?query*}", pathParameters)
+ {
+ }
+
+ ///
+ /// Instantiates a new