diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md index 7596300fb2..d21378826c 100644 --- a/docs/usage/openapi-client.md +++ b/docs/usage/openapi-client.md @@ -32,7 +32,12 @@ The next steps describe how to generate a JSON:API client library and use our pa using var httpClient = new HttpClient(); var apiClient = new ExampleApiClient("http://localhost:14140", httpClient); - PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(); + PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(new Dictionary + { + ["filter"] = "has(assignedTodoItems)", + ["sort"] = "-lastName", + ["page[size]"] = "5" + }); foreach (PersonDataInResponse person in getResponse.Data) { @@ -88,7 +93,8 @@ The next steps describe how to generate a JSON:API client library and use our pa using (apiClient.WithPartialAttributeSerialization(patchRequest, person => person.FirstName)) { - await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, patchRequest)); + // Workaround for https://github.com/RicoSuter/NSwag/issues/2499. + await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, null, patchRequest)); // The sent request looks like this: // { @@ -102,20 +108,6 @@ The next steps describe how to generate a JSON:API client library and use our pa // } // } } - - static async Task TranslateAsync(Func> operation) - where TResponse : class - { - try - { - return await operation(); - } - catch (ApiException exception) when (exception.StatusCode == 204) - { - // Workaround for https://github.com/RicoSuter/NSwag/issues/2499 - return null; - } - } ``` ### Other IDEs diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json b/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json index 797f5e7708..fbefa04d0c 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json +++ b/src/Examples/JsonApiDotNetCoreExampleClient/OpenAPIs/swagger.json @@ -10,10 +10,26 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found people, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -21,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -28,17 +47,30 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/personCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -46,19 +78,40 @@ "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": null + } + } + ], "requestBody": { + "description": "The attributes and relationships of the person to create.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/personPostRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/personPostRequestDocument" + } + ] } } } }, "responses": { "201": { - "description": "Created", + "description": "The person was successfully created, which resulted in additional changes. The newly created person is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -68,7 +121,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." + }, + "409": { + "description": "A resource type in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } } @@ -78,21 +143,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found person.", "content": { "application/vnd.api+json": { "schema": { @@ -100,6 +180,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -107,28 +193,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/personPrimaryResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -136,30 +237,50 @@ "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": { "type": "integer", "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": null + } } ], "requestBody": { + "description": "The attributes and relationships of the person to update. Omitted fields are left unchanged.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/personPatchRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/personPatchRequestDocument" + } + ] } } } }, "responses": { "200": { - "description": "Success", + "description": "The person was successfully updated, which resulted in additional changes. The updated person is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -169,7 +290,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "404": { + "description": "The person or a related resource does not exist." + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } }, @@ -177,11 +310,13 @@ "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": { "type": "integer", @@ -191,7 +326,10 @@ ], "responses": { "204": { - "description": "No Content" + "description": "The person was successfully deleted." + }, + "404": { + "description": "The person does not exist." } } } @@ -201,21 +339,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItems, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -223,6 +376,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -230,28 +389,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } } @@ -261,21 +435,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItem identities, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -283,6 +472,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -290,28 +485,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -319,11 +529,13 @@ "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": { "type": "integer", @@ -332,17 +544,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to add to the assignedTodoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -350,11 +576,13 @@ "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": { "type": "integer", @@ -363,17 +591,31 @@ } ], "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": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The assignedTodoItems relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -381,11 +623,13 @@ "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": { "type": "integer", @@ -394,17 +638,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to remove from the assignedTodoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -414,21 +672,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItems, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -436,6 +709,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -443,28 +722,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } } @@ -474,21 +768,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItem identities, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -496,6 +805,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -503,28 +818,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The person does not exist." } } }, @@ -532,11 +862,13 @@ "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": { "type": "integer", @@ -545,17 +877,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to add to the ownedTodoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -563,11 +909,13 @@ "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": { "type": "integer", @@ -576,17 +924,31 @@ } ], "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": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The ownedTodoItems relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -594,11 +956,13 @@ "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": { "type": "integer", @@ -607,17 +971,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to remove from the ownedTodoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The person does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -627,10 +1005,26 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found tags, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -638,6 +1032,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -645,17 +1042,30 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/tagCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -663,19 +1073,40 @@ "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": null + } + } + ], "requestBody": { + "description": "The attributes and relationships of the tag to create.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/tagPostRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/tagPostRequestDocument" + } + ] } } } }, "responses": { "201": { - "description": "Created", + "description": "The tag was successfully created, which resulted in additional changes. The newly created tag is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -685,7 +1116,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." + }, + "409": { + "description": "A resource type in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } } @@ -695,21 +1138,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found tag.", "content": { "application/vnd.api+json": { "schema": { @@ -717,6 +1175,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } }, @@ -724,28 +1188,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/tagPrimaryResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } }, @@ -753,30 +1232,50 @@ "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": { "type": "integer", "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": null + } } ], "requestBody": { + "description": "The attributes and relationships of the tag to update. Omitted fields are left unchanged.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/tagPatchRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/tagPatchRequestDocument" + } + ] } } } }, "responses": { "200": { - "description": "Success", + "description": "The tag was successfully updated, which resulted in additional changes. The updated tag is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -786,7 +1285,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "404": { + "description": "The tag or a related resource does not exist." + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } }, @@ -794,11 +1305,13 @@ "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": { "type": "integer", @@ -808,7 +1321,10 @@ ], "responses": { "204": { - "description": "No Content" + "description": "The tag was successfully deleted." + }, + "404": { + "description": "The tag does not exist." } } } @@ -818,21 +1334,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItems, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -840,6 +1371,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } }, @@ -847,28 +1384,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } } @@ -878,21 +1430,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItem identities, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -900,6 +1467,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } }, @@ -907,28 +1480,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemIdentifierCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The tag does not exist." } } }, @@ -936,11 +1524,13 @@ "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": { "type": "integer", @@ -949,17 +1539,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to add to the todoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The tag does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -967,11 +1571,13 @@ "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": { "type": "integer", @@ -980,17 +1586,31 @@ } ], "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": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The tag does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -998,11 +1618,13 @@ "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": { "type": "integer", @@ -1011,17 +1633,31 @@ } ], "requestBody": { + "description": "The identities of the todoItems to remove from the todoItems relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The todoItems were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The tag does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -1031,10 +1667,26 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItems, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -1042,6 +1694,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1049,17 +1704,30 @@ "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": null + } + } + ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1067,19 +1735,40 @@ "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": null + } + } + ], "requestBody": { + "description": "The attributes and relationships of the todoItem to create.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/todoItemPostRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemPostRequestDocument" + } + ] } } } }, "responses": { "201": { - "description": "Created", + "description": "The todoItem was successfully created, which resulted in additional changes. The newly created todoItem is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -1089,7 +1778,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." + }, + "409": { + "description": "A resource type in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } } @@ -1099,21 +1800,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found todoItem.", "content": { "application/vnd.api+json": { "schema": { @@ -1121,6 +1837,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1128,28 +1850,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/todoItemPrimaryResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1157,30 +1894,50 @@ "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": { "type": "integer", "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": null + } } ], "requestBody": { + "description": "The attributes and relationships of the todoItem to update. Omitted fields are left unchanged.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/todoItemPatchRequestDocument" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemPatchRequestDocument" + } + ] } } } }, "responses": { "200": { - "description": "Success", + "description": "The todoItem was successfully updated, which resulted in additional changes. The updated todoItem is returned.", "content": { "application/vnd.api+json": { "schema": { @@ -1190,7 +1947,19 @@ } }, "204": { - "description": "No Content" + "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." + }, + "404": { + "description": "The todoItem or a related resource does not exist." + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." } } }, @@ -1198,11 +1967,13 @@ "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": { "type": "integer", @@ -1212,7 +1983,10 @@ ], "responses": { "204": { - "description": "No Content" + "description": "The todoItem was successfully deleted." + }, + "404": { + "description": "The todoItem does not exist." } } } @@ -1222,21 +1996,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found person, or `null` if it was not found.", "content": { "application/vnd.api+json": { "schema": { @@ -1244,6 +2033,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1251,28 +2046,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullablePersonSecondaryResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } } @@ -1282,21 +2092,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found person identity, or `null` if it was not found.", "content": { "application/vnd.api+json": { "schema": { @@ -1304,6 +2129,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1311,28 +2142,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/nullablePersonIdentifierResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1340,11 +2186,13 @@ "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": { "type": "integer", @@ -1353,17 +2201,31 @@ } ], "requestBody": { + "description": "The identity of the person to assign to the assignee relationship, or `null` to clear the relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/nullableToOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOnePersonInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The assignee relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The todoItem does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -1373,21 +2235,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found person, or `null` if it was not found.", "content": { "application/vnd.api+json": { "schema": { @@ -1395,6 +2272,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1402,28 +2285,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/personSecondaryResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } } @@ -1433,21 +2331,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found person identity, or `null` if it was not found.", "content": { "application/vnd.api+json": { "schema": { @@ -1455,6 +2368,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1462,28 +2381,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/personIdentifierResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1491,11 +2425,13 @@ "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": { "type": "integer", @@ -1504,17 +2440,31 @@ } ], "requestBody": { + "description": "The identity of the person to assign to the owner relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toOnePersonInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The owner relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The todoItem does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -1524,21 +2474,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found tags, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -1546,6 +2511,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1553,28 +2524,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/tagCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } } @@ -1584,21 +2570,36 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", + "description": "Successfully returns the found tag identities, or an empty array if none were found.", "content": { "application/vnd.api+json": { "schema": { @@ -1606,6 +2607,12 @@ } } } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1613,28 +2620,43 @@ "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": { "type": "integer", "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": null + } } ], "responses": { "200": { - "description": "Success", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/tagIdentifierCollectionResponseDocument" - } - } - } + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The todoItem does not exist." } } }, @@ -1642,11 +2664,13 @@ "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": { "type": "integer", @@ -1655,17 +2679,31 @@ } ], "requestBody": { + "description": "The identities of the tags to add to the tags relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTagInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The tags were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The todoItem does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -1673,11 +2711,13 @@ "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": { "type": "integer", @@ -1686,17 +2726,31 @@ } ], "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": { "schema": { - "$ref": "#/components/schemas/toManyTagInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The tags relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The todoItem does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } }, @@ -1704,11 +2758,13 @@ "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": { "type": "integer", @@ -1717,17 +2773,31 @@ } ], "requestBody": { + "description": "The identities of the tags to remove from the tags relationship.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/toManyTagInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInRequest" + } + ] } } } }, "responses": { "204": { - "description": "No Content" + "description": "The tags were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The todoItem does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." } } } @@ -1735,40 +2805,15 @@ }, "components": { "schemas": { - "jsonapiObject": { + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], "type": "object", "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "linksInRelationshipObject": { - "required": [ - "related", - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, + "self": { + "minLength": 1, "type": "string" }, "related": { @@ -1780,7 +2825,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -1793,7 +2837,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1826,7 +2869,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -1836,15 +2878,14 @@ "minLength": 1, "type": "string" }, - "describedby": { - "type": "string" - }, "related": { "minLength": 1, "type": "string" }, + "describedby": { + "type": "string" + }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1870,12 +2911,12 @@ "minLength": 1, "type": "string" }, - "describedby": { - "type": "string" - }, "related": { "minLength": 1, "type": "string" + }, + "describedby": { + "type": "string" } }, "additionalProperties": false @@ -1893,29 +2934,6 @@ }, "additionalProperties": false }, - "nullValue": { - "not": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "object" - }, - { - "type": "array" - } - ], - "items": { } - }, - "nullable": true - }, "nullablePersonIdentifierResponseDocument": { "required": [ "data", @@ -1923,25 +2941,27 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + ] + }, "data": { - "oneOf": [ + "allOf": [ { "$ref": "#/components/schemas/personIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" } - ] + ], + "nullable": true }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1953,25 +2973,27 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, "data": { - "oneOf": [ + "allOf": [ { "$ref": "#/components/schemas/personDataInResponse" - }, - { - "$ref": "#/components/schemas/nullValue" } - ] + ], + "nullable": true }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1983,14 +3005,12 @@ "type": "object", "properties": { "data": { - "oneOf": [ + "allOf": [ { "$ref": "#/components/schemas/personIdentifier" - }, - { - "$ref": "#/components/schemas/nullValue" } - ] + ], + "nullable": true } }, "additionalProperties": false @@ -2001,22 +3021,27 @@ ], "type": "object", "properties": { - "data": { - "oneOf": [ - { - "$ref": "#/components/schemas/personIdentifier" - }, + "links": { + "allOf": [ { - "$ref": "#/components/schemas/nullValue" + "$ref": "#/components/schemas/linksInRelationshipObject" } ] }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/personIdentifier" + } + ], + "nullable": true }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2051,10 +3076,6 @@ "additionalProperties": false }, "personAttributesInResponse": { - "required": [ - "displayName", - "lastName" - ], "type": "object", "properties": { "firstName": { @@ -2078,6 +3099,13 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, "data": { "type": "array", "items": { @@ -2086,13 +3114,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2105,17 +3130,29 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/personResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/personResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/personAttributesInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personAttributesInPatchRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/personRelationshipsInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personRelationshipsInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2127,13 +3164,25 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/personResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/personResourceType" + } + ] }, "attributes": { - "$ref": "#/components/schemas/personAttributesInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personAttributesInPostRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/personRelationshipsInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personRelationshipsInPostRequest" + } + ] } }, "additionalProperties": false @@ -2147,24 +3196,43 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/personResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/personResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/personAttributesInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/personAttributesInResponse" + } + ] }, "relationships": { - "$ref": "#/components/schemas/personRelationshipsInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/personRelationshipsInResponse" + } + ] }, "links": { - "$ref": "#/components/schemas/linksInResourceObject" + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceObject" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2177,7 +3245,11 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/personResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/personResourceType" + } + ] }, "id": { "minLength": 1, @@ -2193,18 +3265,26 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + ] + }, "data": { - "$ref": "#/components/schemas/personIdentifier" + "allOf": [ + { + "$ref": "#/components/schemas/personIdentifier" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2216,7 +3296,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/personDataInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personDataInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2228,7 +3312,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/personDataInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/personDataInPostRequest" + } + ] } }, "additionalProperties": false @@ -2240,18 +3328,26 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, "data": { - "$ref": "#/components/schemas/personDataInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/personDataInResponse" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2260,10 +3356,18 @@ "type": "object", "properties": { "ownedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] }, "assignedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } }, "additionalProperties": false @@ -2272,10 +3376,18 @@ "type": "object", "properties": { "ownedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] }, "assignedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } }, "additionalProperties": false @@ -2284,10 +3396,18 @@ "type": "object", "properties": { "ownedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInResponse" + } + ] }, "assignedTodoItems": { - "$ref": "#/components/schemas/toManyTodoItemInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInResponse" + } + ] } }, "additionalProperties": false @@ -2305,18 +3425,26 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, "data": { - "$ref": "#/components/schemas/personDataInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/personDataInResponse" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2345,9 +3473,6 @@ "additionalProperties": false }, "tagAttributesInResponse": { - "required": [ - "name" - ], "type": "object", "properties": { "name": { @@ -2364,6 +3489,13 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, "data": { "type": "array", "items": { @@ -2372,13 +3504,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2391,17 +3520,29 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/tagResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/tagResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/tagAttributesInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagAttributesInPatchRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/tagRelationshipsInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagRelationshipsInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2413,13 +3554,25 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/tagResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/tagResourceType" + } + ] }, "attributes": { - "$ref": "#/components/schemas/tagAttributesInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagAttributesInPostRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/tagRelationshipsInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagRelationshipsInPostRequest" + } + ] } }, "additionalProperties": false @@ -2433,24 +3586,43 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/tagResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/tagResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/tagAttributesInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/tagAttributesInResponse" + } + ] }, "relationships": { - "$ref": "#/components/schemas/tagRelationshipsInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/tagRelationshipsInResponse" + } + ] }, "links": { - "$ref": "#/components/schemas/linksInResourceObject" + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceObject" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2463,7 +3635,11 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/tagResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/tagResourceType" + } + ] }, "id": { "minLength": 1, @@ -2479,6 +3655,13 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, "data": { "type": "array", "items": { @@ -2487,13 +3670,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2505,7 +3685,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/tagDataInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagDataInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2517,7 +3701,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/tagDataInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/tagDataInPostRequest" + } + ] } }, "additionalProperties": false @@ -2529,18 +3717,26 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, "data": { - "$ref": "#/components/schemas/tagDataInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/tagDataInResponse" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2549,7 +3745,11 @@ "type": "object", "properties": { "todoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } }, "additionalProperties": false @@ -2558,7 +3758,11 @@ "type": "object", "properties": { "todoItems": { - "$ref": "#/components/schemas/toManyTodoItemInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInRequest" + } + ] } }, "additionalProperties": false @@ -2567,7 +3771,11 @@ "type": "object", "properties": { "todoItems": { - "$ref": "#/components/schemas/toManyTodoItemInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTodoItemInResponse" + } + ] } }, "additionalProperties": false @@ -2599,18 +3807,25 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/tagIdentifier" } }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2636,18 +3851,25 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/todoItemIdentifier" } }, - "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" - }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2659,7 +3881,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/personIdentifier" + "allOf": [ + { + "$ref": "#/components/schemas/personIdentifier" + } + ] } }, "additionalProperties": false @@ -2670,15 +3896,26 @@ ], "type": "object", "properties": { - "data": { - "$ref": "#/components/schemas/personIdentifier" - }, "links": { - "$ref": "#/components/schemas/linksInRelationshipObject" + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/personIdentifier" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2690,7 +3927,11 @@ "type": "string" }, "priority": { - "$ref": "#/components/schemas/todoItemPriority" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemPriority" + } + ] }, "durationInHours": { "type": "integer", @@ -2711,7 +3952,11 @@ "type": "string" }, "priority": { - "$ref": "#/components/schemas/todoItemPriority" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemPriority" + } + ] }, "durationInHours": { "type": "integer", @@ -2722,17 +3967,17 @@ "additionalProperties": false }, "todoItemAttributesInResponse": { - "required": [ - "description", - "priority" - ], "type": "object", "properties": { "description": { "type": "string" }, "priority": { - "$ref": "#/components/schemas/todoItemPriority" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemPriority" + } + ] }, "durationInHours": { "type": "integer", @@ -2758,6 +4003,13 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, "data": { "type": "array", "items": { @@ -2766,13 +4018,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2785,17 +4034,29 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/todoItemResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/todoItemAttributesInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemAttributesInPatchRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/todoItemRelationshipsInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemRelationshipsInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2807,13 +4068,25 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/todoItemResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemResourceType" + } + ] }, "attributes": { - "$ref": "#/components/schemas/todoItemAttributesInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemAttributesInPostRequest" + } + ] }, "relationships": { - "$ref": "#/components/schemas/todoItemRelationshipsInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemRelationshipsInPostRequest" + } + ] } }, "additionalProperties": false @@ -2827,24 +4100,43 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/todoItemResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemResourceType" + } + ] }, "id": { "minLength": 1, "type": "string" }, "attributes": { - "$ref": "#/components/schemas/todoItemAttributesInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemAttributesInResponse" + } + ] }, "relationships": { - "$ref": "#/components/schemas/todoItemRelationshipsInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemRelationshipsInResponse" + } + ] }, "links": { - "$ref": "#/components/schemas/linksInResourceObject" + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceObject" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2857,7 +4149,11 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/todoItemResourceType" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemResourceType" + } + ] }, "id": { "minLength": 1, @@ -2873,6 +4169,13 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, "data": { "type": "array", "items": { @@ -2881,13 +4184,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2899,7 +4199,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/todoItemDataInPatchRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemDataInPatchRequest" + } + ] } }, "additionalProperties": false @@ -2911,7 +4215,11 @@ "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/todoItemDataInPostRequest" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemDataInPostRequest" + } + ] } }, "additionalProperties": false @@ -2923,18 +4231,26 @@ ], "type": "object", "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, "data": { - "$ref": "#/components/schemas/todoItemDataInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/todoItemDataInResponse" + } + ] }, "meta": { "type": "object", - "additionalProperties": { } - }, - "jsonapi": { - "$ref": "#/components/schemas/jsonapiObject" - }, - "links": { - "$ref": "#/components/schemas/linksInResourceDocument" + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2951,13 +4267,25 @@ "type": "object", "properties": { "owner": { - "$ref": "#/components/schemas/toOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toOnePersonInRequest" + } + ] }, "assignee": { - "$ref": "#/components/schemas/nullableToOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOnePersonInRequest" + } + ] }, "tags": { - "$ref": "#/components/schemas/toManyTagInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInRequest" + } + ] } }, "additionalProperties": false @@ -2969,31 +4297,52 @@ "type": "object", "properties": { "owner": { - "$ref": "#/components/schemas/toOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toOnePersonInRequest" + } + ] }, "assignee": { - "$ref": "#/components/schemas/nullableToOnePersonInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOnePersonInRequest" + } + ] }, "tags": { - "$ref": "#/components/schemas/toManyTagInRequest" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInRequest" + } + ] } }, "additionalProperties": false }, "todoItemRelationshipsInResponse": { - "required": [ - "owner" - ], "type": "object", "properties": { "owner": { - "$ref": "#/components/schemas/toOnePersonInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/toOnePersonInResponse" + } + ] }, "assignee": { - "$ref": "#/components/schemas/nullableToOnePersonInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOnePersonInResponse" + } + ] }, "tags": { - "$ref": "#/components/schemas/toManyTagInResponse" + "allOf": [ + { + "$ref": "#/components/schemas/toManyTagInResponse" + } + ] } }, "additionalProperties": false diff --git a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs index 1ef318fa65..6424fd45e5 100644 --- a/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs +++ b/src/Examples/JsonApiDotNetCoreExampleClient/Program.cs @@ -3,7 +3,12 @@ using var httpClient = new HttpClient(); var apiClient = new ExampleApiClient("http://localhost:14140", httpClient); -PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(); +PersonCollectionResponseDocument getResponse = await apiClient.GetPersonCollectionAsync(new Dictionary +{ + ["filter"] = "has(assignedTodoItems)", + ["sort"] = "-lastName", + ["page[size]"] = "5" +}); foreach (PersonDataInResponse person in getResponse.Data) { @@ -25,7 +30,8 @@ // This line results in sending "firstName: null" instead of omitting it. using (apiClient.WithPartialAttributeSerialization(patchRequest, person => person.FirstName)) { - await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, patchRequest)); + // Workaround for https://github.com/RicoSuter/NSwag/issues/2499. + await TranslateAsync(async () => await apiClient.PatchPersonAsync(1, null, patchRequest)); } Console.WriteLine("Press any key to close."); diff --git a/src/Examples/QueryStringExample/swagger.json b/src/Examples/QueryStringExample/swagger.json new file mode 100644 index 0000000000..819aafdb8f --- /dev/null +++ b/src/Examples/QueryStringExample/swagger.json @@ -0,0 +1,1602 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "QueryStringExample", + "version": "1.0" + }, + "paths": { + "/api/nodes": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves a collection of nodes.", + "operationId": "getNodeCollection", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found nodes, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves a collection of nodes 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": "headNodeCollection", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "post": { + "tags": [ + "nodes" + ], + "summary": "Creates a new node.", + "operationId": "postNode", + "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": null + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the node to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nodePostRequestDocument" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The node was successfully created, which resulted in additional changes. The newly created node is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The node 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." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." + }, + "409": { + "description": "A resource type in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." + } + } + } + }, + "/api/nodes/{id}": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves an individual node by its identifier.", + "operationId": "getNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves an individual node 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": "headNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Updates an existing node.", + "operationId": "patchNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to update.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the node to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nodePatchRequestDocument" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "The node was successfully updated, which resulted in additional changes. The updated node is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The node 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." + }, + "404": { + "description": "The node or a related resource does not exist." + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." + } + } + }, + "delete": { + "tags": [ + "nodes" + ], + "summary": "Deletes an existing node by its identifier.", + "operationId": "deleteNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to delete.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "The node was successfully deleted." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/api/nodes/{id}/children": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related nodes of an individual node's children relationship.", + "operationId": "getNodeChildren", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related nodes to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found nodes, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related nodes of an individual node's children 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": "headNodeChildren", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related nodes to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/api/nodes/{id}/relationships/children": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identities of an individual node's children relationship.", + "operationId": "getNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identities to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node identities, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeIdentifierCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identities of an individual node's children 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": "headNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identities to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "post": { + "tags": [ + "nodes" + ], + "summary": "Adds existing nodes to the children relationship of an individual node.", + "operationId": "postNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to add nodes to.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to add to the children relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The nodes were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Assigns existing nodes to the children relationship of an individual node.", + "operationId": "patchNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose children relationship to assign.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to assign to the children relationship, or an empty array to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The children relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + }, + "delete": { + "tags": [ + "nodes" + ], + "summary": "Removes existing nodes from the children relationship of an individual node.", + "operationId": "deleteNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to remove nodes from.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to remove from the children relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The nodes were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + } + }, + "/api/nodes/{id}/parent": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node of an individual node's parent relationship.", + "operationId": "getNodeParent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node, or `null` if it was not found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableNodeSecondaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node of an individual node's parent 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": "headNodeParent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/api/nodes/{id}/relationships/parent": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identity of an individual node's parent relationship.", + "operationId": "getNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identity to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node identity, or `null` if it was not found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableNodeIdentifierResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identity of an individual node's parent 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": "headNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identity to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Clears or assigns an existing node to the parent relationship of an individual node.", + "operationId": "patchNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose parent relationship to assign or clear.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identity of the node to assign to the parent relationship, or `null` to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The parent relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + } + } + }, + "components": { + "schemas": { + "jsonapiObject": { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "ext": { + "type": "array", + "items": { + "type": "string" + } + }, + "profile": { + "type": "array", + "items": { + "type": "string" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nodeAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeAttributesInPostRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "jsonapi": { + "allOf": [ + { + "$ref": "#/components/schemas/jsonapiObject" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nodeDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInPatchRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInPostRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInResponse" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceObject" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nodeIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nodeIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "jsonapi": { + "allOf": [ + { + "$ref": "#/components/schemas/jsonapiObject" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nodePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "jsonapi": { + "allOf": [ + { + "$ref": "#/components/schemas/jsonapiObject" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInResponse" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInPostRequest": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInResponse": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInResponse" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInResponse" + } + ] + } + }, + "additionalProperties": false + }, + "nodeResourceType": { + "enum": [ + "nodes" + ], + "type": "string" + }, + "nullableNodeIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "jsonapi": { + "allOf": [ + { + "$ref": "#/components/schemas/jsonapiObject" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nullableNodeSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "jsonapi": { + "allOf": [ + { + "$ref": "#/components/schemas/jsonapiObject" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInResponse" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "nullableToOneNodeInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + "nullableToOneNodeInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + }, + "toManyNodeInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyNodeInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiClientTests/ApiResponse.cs b/src/JsonApiDotNetCore.OpenApi.Client/ApiResponse.cs similarity index 83% rename from test/OpenApiClientTests/ApiResponse.cs rename to src/JsonApiDotNetCore.OpenApi.Client/ApiResponse.cs index 10e9ecf6ca..a94e5062dc 100644 --- a/test/OpenApiClientTests/ApiResponse.cs +++ b/src/JsonApiDotNetCore.OpenApi.Client/ApiResponse.cs @@ -1,11 +1,12 @@ -using JsonApiDotNetCore.OpenApi.Client; +using JetBrains.Annotations; using JsonApiDotNetCore.OpenApi.Client.Exceptions; #pragma warning disable AV1008 // Class should not be static -namespace OpenApiClientTests; +namespace JsonApiDotNetCore.OpenApi.Client; -internal static class ApiResponse +[PublicAPI] +public static class ApiResponse { public static async Task TranslateAsync(Func> operation) where TResponse : class diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs index 9ce49f9f58..ee9c9d31df 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceCollectionDocument.cs @@ -14,7 +14,6 @@ internal sealed class LinksInResourceCollectionDocument [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; - [Required] [JsonPropertyName("first")] public string First { get; set; } = null!; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs index 839dde99b8..ca32345e55 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs @@ -18,7 +18,6 @@ internal sealed class LinksInResourceIdentifierCollectionDocument [JsonPropertyName("describedby")] public string Describedby { get; set; } = null!; - [Required] [JsonPropertyName("first")] public string First { get; set; } = null!; diff --git a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs index 507cd26e59..2da76a3931 100644 --- a/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs +++ b/src/JsonApiDotNetCore.OpenApi/JsonApiOperationIdSelector.cs @@ -56,14 +56,14 @@ public string GetOperationId(ApiDescription endpoint) throw new UnreachableCodeException(); } - string template = GetTemplate(primaryResourceType.ClrType, endpoint); + string template = GetTemplate(endpoint); return ApplyTemplate(template, primaryResourceType, endpoint); } - private static string GetTemplate(Type resourceClrType, ApiDescription endpoint) + private static string GetTemplate(ApiDescription endpoint) { - Type requestDocumentType = GetDocumentType(resourceClrType, endpoint); + Type requestDocumentType = GetDocumentType(endpoint); if (!DocumentOpenTypeToOperationIdTemplateMap.TryGetValue(requestDocumentType, out string? template)) { @@ -73,7 +73,7 @@ private static string GetTemplate(Type resourceClrType, ApiDescription endpoint) return template; } - private static Type GetDocumentType(Type primaryResourceClrType, ApiDescription endpoint) + private static Type GetDocumentType(ApiDescription endpoint) { var producesResponseTypeAttribute = endpoint.ActionDescriptor.GetFilterMetadata(); @@ -87,14 +87,9 @@ private static Type GetDocumentType(Type primaryResourceClrType, ApiDescription Type documentType = requestBodyDescriptor?.ParameterType.GetGenericTypeDefinition() ?? GetGenericTypeDefinition(producesResponseTypeAttribute.Type) ?? producesResponseTypeAttribute.Type; - if (documentType == typeof(ResourceCollectionResponseDocument<>)) + if (documentType == typeof(ResourceCollectionResponseDocument<>) && endpoint.ParameterDescriptions.Count > 0) { - Type documentResourceType = producesResponseTypeAttribute.Type.GetGenericArguments()[0]; - - if (documentResourceType != primaryResourceClrType) - { - documentType = typeof(SecondaryResourceResponseDocument<>); - } + documentType = typeof(SecondaryResourceResponseDocument<>); } return documentType; diff --git a/src/JsonApiDotNetCore.OpenApi/OpenApiSchemaExtensions.cs b/src/JsonApiDotNetCore.OpenApi/OpenApiSchemaExtensions.cs index e86c2c7423..8894d68600 100644 --- a/src/JsonApiDotNetCore.OpenApi/OpenApiSchemaExtensions.cs +++ b/src/JsonApiDotNetCore.OpenApi/OpenApiSchemaExtensions.cs @@ -4,6 +4,29 @@ namespace JsonApiDotNetCore.OpenApi; internal static class OpenApiSchemaExtensions { + public static void ReorderProperties(this OpenApiSchema fullSchema, IEnumerable propertyNamesInOrder) + { + ArgumentGuard.NotNull(fullSchema); + ArgumentGuard.NotNull(propertyNamesInOrder); + + var propertiesInOrder = new Dictionary(); + + foreach (string propertyName in propertyNamesInOrder) + { + if (fullSchema.Properties.TryGetValue(propertyName, out OpenApiSchema? schema)) + { + propertiesInOrder.Add(propertyName, schema); + } + } + + if (fullSchema.Properties.Count != propertiesInOrder.Count) + { + throw new UnreachableCodeException(); + } + + fullSchema.Properties = propertiesInOrder; + } + public static OpenApiSchema UnwrapExtendedReferenceSchema(this OpenApiSchema source) { ArgumentGuard.NotNull(source); @@ -15,4 +38,18 @@ public static OpenApiSchema UnwrapExtendedReferenceSchema(this OpenApiSchema sou return source.AllOf.Single(); } + + public static void SetValuesInMetaToNullable(this OpenApiSchema fullSchema) + { + ArgumentGuard.NotNull(fullSchema); + + if (fullSchema.Properties.TryGetValue(JsonApiPropertyName.Meta, out OpenApiSchema? schemaForMeta)) + { + schemaForMeta.AdditionalProperties = new OpenApiSchema + { + Type = "object", + Nullable = true + }; + } + } } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiOperationDocumentationFilter.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiOperationDocumentationFilter.cs index a5abe45263..6421b56c07 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiOperationDocumentationFilter.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiOperationDocumentationFilter.cs @@ -7,6 +7,7 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; @@ -29,12 +30,20 @@ internal sealed class JsonApiOperationDocumentationFilter : IOperationFilter "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched."; private const string TextCompletedSuccessfully = "The operation completed successfully."; - private const string TextRequestBodyMissingOrMalformed = "The request body is missing or malformed."; + private const string TextQueryStringBad = "The query string is invalid."; + private const string TextRequestBodyBad = "The request body is missing or malformed."; + private const string TextQueryStringOrRequestBodyBad = "The query string is invalid or the request body is missing or malformed."; private const string TextRequestBodyIncompatibleType = "A resource type in the request body is incompatible."; private const string TextRequestBodyIncompatibleIdOrType = "A resource type or identifier in the request body is incompatible."; private const string TextRequestBodyValidationFailed = "Validation of the request body failed."; private const string TextRequestBodyClientId = "Client-generated IDs cannot be used at this endpoint."; + private const string TextQueryStringParameters = + "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."; + private readonly IJsonApiOptions _options; private readonly IControllerResourceMapping _controllerResourceMapping; private readonly ResourceFieldValidationMetadataProvider _resourceFieldValidationMetadataProvider; @@ -156,6 +165,9 @@ private static void ApplyGetPrimary(OpenApiOperation operation, ResourceType res SetResponseDescription(operation.Responses, HttpStatusCode.OK, $"Successfully returns the found {resourceType}, or an empty array if none were found."); } + + AddQueryStringParameters(operation); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); } else if (operation.Parameters.Count == 1) { @@ -174,6 +186,8 @@ private static void ApplyGetPrimary(OpenApiOperation operation, ResourceType res } SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularName} to retrieve."); + AddQueryStringParameters(operation); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularName} does not exist."); } } @@ -183,6 +197,7 @@ private void ApplyPostResource(OpenApiOperation operation, ResourceType resource string singularName = resourceType.PublicName.Singularize(); SetOperationSummary(operation, $"Creates a new {singularName}."); + AddQueryStringParameters(operation); SetRequestBodyDescription(operation.RequestBody, $"The attributes and relationships of the {singularName} to create."); SetResponseDescription(operation.Responses, HttpStatusCode.Created, @@ -191,9 +206,7 @@ private void ApplyPostResource(OpenApiOperation operation, ResourceType resource SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {singularName} was successfully created, which did not result in additional changes."); - SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyMissingOrMalformed); - SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleType); - SetResponseDescription(operation.Responses, HttpStatusCode.UnprocessableEntity, TextRequestBodyValidationFailed); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringOrRequestBodyBad); ClientIdGenerationMode clientIdGeneration = resourceType.ClientIdGeneration ?? _options.ClientIdGeneration; @@ -201,6 +214,9 @@ private void ApplyPostResource(OpenApiOperation operation, ResourceType resource { SetResponseDescription(operation.Responses, HttpStatusCode.Forbidden, TextRequestBodyClientId); } + + SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleType); + SetResponseDescription(operation.Responses, HttpStatusCode.UnprocessableEntity, TextRequestBodyValidationFailed); } private void ApplyPatchResource(OpenApiOperation operation, ResourceType resourceType) @@ -209,6 +225,7 @@ private void ApplyPatchResource(OpenApiOperation operation, ResourceType resourc SetOperationSummary(operation, $"Updates an existing {singularName}."); SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularName} to update."); + AddQueryStringParameters(operation); SetRequestBodyDescription(operation.RequestBody, $"The attributes and relationships of the {singularName} to update. Omitted fields are left unchanged."); @@ -219,8 +236,8 @@ private void ApplyPatchResource(OpenApiOperation operation, ResourceType resourc SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {singularName} was successfully updated, which did not result in additional changes."); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringOrRequestBodyBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularName} or a related resource does not exist."); - SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyMissingOrMalformed); SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleIdOrType); SetResponseDescription(operation.Responses, HttpStatusCode.UnprocessableEntity, TextRequestBodyValidationFailed); } @@ -261,6 +278,8 @@ relationship is HasOneAttribute } SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} whose related {rightName} to retrieve."); + AddQueryStringParameters(operation); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularLeftName} does not exist."); } @@ -292,6 +311,8 @@ relationship is HasOneAttribute } SetParameterDescription(operation.Parameters[0], $"The identifier of the {singularLeftName} whose related {singularRightName} {ident} to retrieve."); + AddQueryStringParameters(operation); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextQueryStringBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularLeftName} does not exist."); } @@ -307,8 +328,8 @@ private void ApplyPostRelationship(OpenApiOperation operation, RelationshipAttri SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {rightName} were successfully added, which did not result in additional changes."); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularLeftName} does not exist."); - SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyMissingOrMalformed); SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleType); } @@ -340,8 +361,8 @@ relationship is HasOneAttribute SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {relationship} relationship was successfully updated, which did not result in additional changes."); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularLeftName} does not exist."); - SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyMissingOrMalformed); SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleType); } @@ -357,8 +378,8 @@ private void ApplyDeleteRelationship(OpenApiOperation operation, RelationshipAtt SetResponseDescription(operation.Responses, HttpStatusCode.NoContent, $"The {rightName} were successfully removed, which did not result in additional changes."); + SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyBad); SetResponseDescription(operation.Responses, HttpStatusCode.NotFound, $"The {singularLeftName} does not exist."); - SetResponseDescription(operation.Responses, HttpStatusCode.BadRequest, TextRequestBodyMissingOrMalformed); SetResponseDescription(operation.Responses, HttpStatusCode.Conflict, TextRequestBodyIncompatibleType); } @@ -405,4 +426,35 @@ private static void SetResponseDescription(OpenApiResponses responses, HttpStatu response.Description = XmlCommentsTextHelper.Humanize(description); } + + private static void AddQueryStringParameters(OpenApiOperation operation) + { + // The JSON:API query string parameters (include, filter, sort, page[size], page[number], fields[]) are too dynamic to represent in OpenAPI. + // - The parameter names for fields[] require exploding to all resource types, because outcome of possible resource types depends on + // the relationship chains in include, which are provided at invocation time. + // - The parameter names for filter/sort take a relationship path, which could be infinite. For example: ?filter[node.parent.parent.parent...]=... + + // The next best thing is to expose the query string parameters as unstructured and optional. + // - This makes SwaggerUI ask for JSON, which is a bit odd, but it works. For example: {"sort":"-id"} produces: ?sort=-id + // - This makes NSwag produce a C# client with method signature: GetAsync(IDictionary? query) + // when combined with /GenerateNullableReferenceTypes:true in the project file. + + operation.Parameters.Add(new OpenApiParameter + { + In = ParameterLocation.Query, + Name = "query", + Schema = new OpenApiSchema + { + Type = "object", + AdditionalProperties = new OpenApiSchema + { + Type = "string", + Nullable = true + }, + // Prevent SwaggerUI from producing sample, which fails when used because unknown query string parameters are blocked by default. + Example = new OpenApiNull() + }, + Description = TextQueryStringParameters + }); + } } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs index 0a3b3c01b5..f401e11f52 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs @@ -83,17 +83,17 @@ public OpenApiSchema GenerateSchema(Type modelType, SchemaRepository schemaRepos if (IsJsonApiDocument(modelType)) { - OpenApiSchema schema = GenerateJsonApiDocumentSchema(modelType); + OpenApiSchema referenceSchemaForDocument = GenerateJsonApiDocumentSchema(modelType); + OpenApiSchema fullSchemaForDocument = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForDocument.Reference.Id]; if (IsDataPropertyNullableInDocument(modelType)) { - SetDataObjectSchemaToNullable(schema); + SetDataObjectSchemaToNullable(fullSchemaForDocument); } - if (!_options.IncludeJsonApiVersion) - { - RemoveJsonApiObject(schema); - } + fullSchemaForDocument.SetValuesInMetaToNullable(); + + SetJsonApiVersion(fullSchemaForDocument); // Schema might depend on other schemas not handled by us, so should not return here. } @@ -150,20 +150,30 @@ private static OpenApiSchema CreateArrayTypeDataSchema(OpenApiSchema referenceSc }; } - private void SetDataObjectSchemaToNullable(OpenApiSchema referenceSchemaForDocument) + private void SetDataObjectSchemaToNullable(OpenApiSchema fullSchemaForDocument) { - OpenApiSchema fullSchemaForDocument = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForDocument.Reference.Id]; OpenApiSchema referenceSchemaForData = fullSchemaForDocument.Properties[JsonApiPropertyName.Data]; referenceSchemaForData.Nullable = true; fullSchemaForDocument.Properties[JsonApiPropertyName.Data] = referenceSchemaForData; } - private void RemoveJsonApiObject(OpenApiSchema referenceSchemaForDocument) + private void SetJsonApiVersion(OpenApiSchema fullSchemaForDocument) { - OpenApiSchema fullSchemaForDocument = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForDocument.Reference.Id]; - fullSchemaForDocument.Properties.Remove(JsonApiPropertyName.Jsonapi); + if (fullSchemaForDocument.Properties.TryGetValue(JsonApiPropertyName.Jsonapi, out OpenApiSchema? referenceSchemaForJsonapi)) + { + string jsonapiSchemaId = referenceSchemaForJsonapi.AllOf[0].Reference.Id; - _schemaRepositoryAccessor.Current.Schemas.Remove("jsonapi-object"); + if (!_options.IncludeJsonApiVersion) + { + fullSchemaForDocument.Properties.Remove(JsonApiPropertyName.Jsonapi); + _schemaRepositoryAccessor.Current.Schemas.Remove(jsonapiSchemaId); + } + else + { + OpenApiSchema fullSchemaForJsonapi = _schemaRepositoryAccessor.Current.Schemas[jsonapiSchemaId]; + fullSchemaForJsonapi.SetValuesInMetaToNullable(); + } + } } private static OpenApiSchema CreateExtendedReferenceSchema(OpenApiSchema referenceSchemaForResourceObject) diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/OpenApiSchemaExtensions.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/OpenApiSchemaExtensions.cs deleted file mode 100644 index d49abce453..0000000000 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/OpenApiSchemaExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.OpenApi.Models; - -namespace JsonApiDotNetCore.OpenApi.SwaggerComponents; - -internal static class OpenApiSchemaExtensions -{ - public static void ReorderProperties(this OpenApiSchema fullSchemaForResourceObject, IEnumerable propertyNamesInOrder) - { - ArgumentGuard.NotNull(fullSchemaForResourceObject); - ArgumentGuard.NotNull(propertyNamesInOrder); - - var propertiesInOrder = new Dictionary(); - - foreach (string propertyName in propertyNamesInOrder) - { - if (fullSchemaForResourceObject.Properties.TryGetValue(propertyName, out OpenApiSchema? schema)) - { - propertiesInOrder.Add(propertyName, schema); - } - } - - if (fullSchemaForResourceObject.Properties.Count != propertiesInOrder.Count) - { - throw new UnreachableCodeException(); - } - - fullSchemaForResourceObject.Properties = propertiesInOrder; - } -} diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs index 4011c357ac..c04162a1dc 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs @@ -145,9 +145,8 @@ private Type GetRepresentedTypeForAttributeSchema(AttrAttribute attribute) private bool IsFieldRequired(ResourceFieldAttribute field) { - bool isSchemaForUpdateResourceEndpoint = _resourceTypeInfo.ResourceObjectOpenType == typeof(ResourceObjectInPatchRequest<>); - - return !isSchemaForUpdateResourceEndpoint && _resourceFieldValidationMetadataProvider.IsRequired(field); + bool isSchemaForPostResourceRequest = _resourceTypeInfo.ResourceObjectOpenType == typeof(ResourceObjectInPostRequest<>); + return isSchemaForPostResourceRequest && _resourceFieldValidationMetadataProvider.IsRequired(field); } public void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelationshipsObject) @@ -245,6 +244,8 @@ private OpenApiSchema CreateRelationshipReferenceSchema(Type relationshipSchemaT { fullSchema.Required.Remove(JsonApiPropertyName.Data); + fullSchema.SetValuesInMetaToNullable(); + fullSchema.ReorderProperties(RelationshipObjectPropertyNamesInOrder); } diff --git a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs index 98c35db59b..92764bf45f 100644 --- a/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs +++ b/src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs @@ -59,6 +59,7 @@ public OpenApiSchema GenerateSchema(Type resourceObjectType) RemoveResourceIdIfPostResourceObject(resourceTypeInfo, fullSchemaForResourceObject); SetResourceType(fullSchemaForResourceObject, resourceTypeInfo.ResourceType); + fullSchemaForResourceObject.SetValuesInMetaToNullable(); SetResourceAttributes(fullSchemaForResourceObject, fieldObjectBuilder); diff --git a/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs index be71dfa1e3..6086d85c5b 100644 --- a/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs +++ b/test/OpenApiClientTests/LegacyClient/PartialAttributeSerializationLifetimeTests.cs @@ -1,5 +1,6 @@ using System.Net; using FluentAssertions; +using JsonApiDotNetCore.OpenApi.Client; using OpenApiClientTests.LegacyClient.GeneratedCode; using TestBuildingBlocks; using Xunit; @@ -30,13 +31,13 @@ public async Task Disposed_registration_does_not_affect_request() using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.AirtimeInHours)) { - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); } wrapper.ChangeResponse(HttpStatusCode.NoContent, null); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); // Assert wrapper.RequestBody.Should().BeJson(@"{ @@ -75,14 +76,14 @@ public async Task Registration_can_be_used_for_multiple_requests() using (apiClient.WithPartialAttributeSerialization(requestDocument, airplane => airplane.AirtimeInHours)) { - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); wrapper.ChangeResponse(HttpStatusCode.NoContent, null); requestDocument.Data.Attributes.AirtimeInHours = null; // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); } // Assert @@ -137,7 +138,7 @@ public async Task Request_is_unaffected_by_registration_for_different_document_o } // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, requestDocument2)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, null, requestDocument2)); } // Assert @@ -180,7 +181,7 @@ public async Task Attribute_values_can_be_changed_after_registration() requestDocument.Data.Attributes.IsInMaintenance = false; // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); } // Assert @@ -230,7 +231,7 @@ public async Task Registration_is_unaffected_by_successive_registration_for_docu airplane => airplane.AirtimeInHours)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, requestDocument1)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, null, requestDocument1)); } } @@ -268,7 +269,7 @@ public async Task Registration_is_unaffected_by_preceding_disposed_registration_ using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.AirtimeInHours)) { - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, requestDocument1)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId1, null, requestDocument1)); } const string airplaneId2 = "DJy1u"; @@ -292,7 +293,7 @@ public async Task Registration_is_unaffected_by_preceding_disposed_registration_ airplane => airplane.SerialNumber)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, requestDocument2)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, null, requestDocument2)); } // Assert @@ -330,7 +331,7 @@ public async Task Registration_is_unaffected_by_preceding_disposed_registration_ using (apiClient.WithPartialAttributeSerialization(requestDocument1, airplane => airplane.AirtimeInHours)) { - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(requestDocument1)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(null, requestDocument1)); } const string airplaneId = "DJy1u"; @@ -354,7 +355,7 @@ public async Task Registration_is_unaffected_by_preceding_disposed_registration_ airplane => airplane.SerialNumber)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument2)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument2)); } // Assert @@ -408,7 +409,7 @@ public async Task Registration_is_unaffected_by_preceding_registration_for_diffe airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, requestDocument2)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId2, null, requestDocument2)); } } diff --git a/test/OpenApiClientTests/LegacyClient/RequestTests.cs b/test/OpenApiClientTests/LegacyClient/RequestTests.cs index 628106f4d1..20d91c44a6 100644 --- a/test/OpenApiClientTests/LegacyClient/RequestTests.cs +++ b/test/OpenApiClientTests/LegacyClient/RequestTests.cs @@ -3,6 +3,7 @@ using FluentAssertions.Common; using FluentAssertions.Extensions; using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.OpenApi.Client; using Microsoft.Net.Http.Headers; using OpenApiClientTests.LegacyClient.GeneratedCode; using TestBuildingBlocks; @@ -22,7 +23,7 @@ public async Task Getting_resource_collection_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCollectionAsync()); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCollectionAsync(null)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -42,7 +43,7 @@ public async Task Getting_resource_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightAsync(flightId)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightAsync(flightId, null)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -87,7 +88,7 @@ public async Task Partial_posting_resource_with_selected_relationships_produces_ }; // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostFlightAsync(requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostFlightAsync(null, requestDocument)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -156,7 +157,7 @@ public async Task Partial_posting_resource_produces_expected_request() airplane => airplane.SerialNumber)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PostAirplaneAsync(null, requestDocument)); } // Assert @@ -207,7 +208,7 @@ public async Task Partial_patching_resource_produces_expected_request() airplane => airplane.SerialNumber, airplane => airplane.LastServicedAt, airplane => airplane.IsInMaintenance, airplane => airplane.AirtimeInHours)) { // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, requestDocument)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.PatchAirplaneAsync(airplaneId, null, requestDocument)); } // Assert @@ -262,7 +263,7 @@ public async Task Getting_secondary_resource_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightPurserAsync(flightId)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightPurserAsync(flightId, null)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -282,7 +283,7 @@ public async Task Getting_secondary_resources_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCabinCrewMembersAsync(flightId)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCabinCrewMembersAsync(flightId, null)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -302,7 +303,7 @@ public async Task Getting_ToOne_relationship_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightPurserRelationshipAsync(flightId)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightPurserRelationshipAsync(flightId, null)); // Assert wrapper.Request.ShouldNotBeNull(); @@ -359,7 +360,7 @@ public async Task Getting_ToMany_relationship_produces_expected_request() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCabinCrewMembersRelationshipAsync(flightId)); + _ = await ApiResponse.TranslateAsync(async () => await apiClient.GetFlightCabinCrewMembersRelationshipAsync(flightId, null)); // Assert wrapper.Request.ShouldNotBeNull(); diff --git a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs index 73f1a9d0e5..c837d8bb50 100644 --- a/test/OpenApiClientTests/LegacyClient/ResponseTests.cs +++ b/test/OpenApiClientTests/LegacyClient/ResponseTests.cs @@ -2,6 +2,7 @@ using System.Net; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using JsonApiDotNetCore.OpenApi.Client.Exceptions; using OpenApiClientTests.LegacyClient.GeneratedCode; using Xunit; @@ -96,7 +97,7 @@ public async Task Getting_resource_collection_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightCollectionResponseDocument document = await apiClient.GetFlightCollectionAsync(); + FlightCollectionResponseDocument document = await apiClient.GetFlightCollectionAsync(null); // Assert document.Jsonapi.Should().BeNull(); @@ -176,7 +177,7 @@ public async Task Getting_resource_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightPrimaryResponseDocument document = await apiClient.GetFlightAsync(flightId); + FlightPrimaryResponseDocument document = await apiClient.GetFlightAsync(flightId, null); // Assert document.Jsonapi.Should().BeNull(); @@ -212,7 +213,7 @@ public async Task Getting_unknown_resource_translates_error_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.GetFlightAsync(flightId); + Func> action = async () => await apiClient.GetFlightAsync(flightId, null); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -276,7 +277,7 @@ public async Task Posting_resource_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightPrimaryResponseDocument document = await apiClient.PostFlightAsync(new FlightPostRequestDocument + FlightPrimaryResponseDocument document = await apiClient.PostFlightAsync(null, new FlightPostRequestDocument { Data = new FlightDataInPostRequest { @@ -328,7 +329,7 @@ public async Task Patching_resource_with_side_effects_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightPrimaryResponseDocument document = await apiClient.PatchFlightAsync(flightId, new FlightPatchRequestDocument + FlightPrimaryResponseDocument document = await apiClient.PatchFlightAsync(flightId, null, new FlightPatchRequestDocument { Data = new FlightDataInPatchRequest { @@ -352,7 +353,7 @@ public async Task Patching_resource_without_side_effects_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightPrimaryResponseDocument? document = await ApiResponse.TranslateAsync(async () => await apiClient.PatchFlightAsync(flightId, + FlightPrimaryResponseDocument? document = await ApiResponse.TranslateAsync(async () => await apiClient.PatchFlightAsync(flightId, null, new FlightPatchRequestDocument { Data = new FlightDataInPatchRequest @@ -430,7 +431,7 @@ public async Task Getting_secondary_resource_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightAttendantSecondaryResponseDocument document = await apiClient.GetFlightPurserAsync(flightId); + FlightAttendantSecondaryResponseDocument document = await apiClient.GetFlightPurserAsync(flightId, null); // Assert document.Data.Should().NotBeNull(); @@ -460,7 +461,7 @@ public async Task Getting_nullable_secondary_resource_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - NullableFlightAttendantSecondaryResponseDocument document = await apiClient.GetFlightBackupPurserAsync(flightId); + NullableFlightAttendantSecondaryResponseDocument document = await apiClient.GetFlightBackupPurserAsync(flightId, null); // Assert document.Data.Should().BeNull(); @@ -484,7 +485,7 @@ public async Task Getting_secondary_resources_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightAttendantCollectionResponseDocument document = await apiClient.GetFlightCabinCrewMembersAsync(flightId); + FlightAttendantCollectionResponseDocument document = await apiClient.GetFlightCabinCrewMembersAsync(flightId, null); // Assert document.Data.Should().BeEmpty(); @@ -508,7 +509,7 @@ public async Task Getting_nullable_ToOne_relationship_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - NullableFlightAttendantIdentifierResponseDocument document = await apiClient.GetFlightBackupPurserRelationshipAsync(flightId); + NullableFlightAttendantIdentifierResponseDocument document = await apiClient.GetFlightBackupPurserRelationshipAsync(flightId, null); // Assert document.Data.Should().BeNull(); @@ -536,7 +537,7 @@ public async Task Getting_ToOne_relationship_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightAttendantIdentifierResponseDocument document = await apiClient.GetFlightPurserRelationshipAsync(flightId); + FlightAttendantIdentifierResponseDocument document = await apiClient.GetFlightPurserRelationshipAsync(flightId, null); // Assert document.Data.Should().NotBeNull(); @@ -590,7 +591,7 @@ public async Task Getting_ToMany_relationship_translates_response() IOpenApiClient apiClient = new OpenApiClient(wrapper.HttpClient); // Act - FlightAttendantIdentifierCollectionResponseDocument document = await apiClient.GetFlightCabinCrewMembersRelationshipAsync(flightId); + FlightAttendantIdentifierCollectionResponseDocument document = await apiClient.GetFlightCabinCrewMembersRelationshipAsync(flightId, null); // Assert document.Data.Should().HaveCount(2); diff --git a/test/OpenApiClientTests/LegacyClient/swagger.g.json b/test/OpenApiClientTests/LegacyClient/swagger.g.json index e95d8261b4..e014464a4c 100644 --- a/test/OpenApiClientTests/LegacyClient/swagger.g.json +++ b/test/OpenApiClientTests/LegacyClient/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of airplanes.", "operationId": "get-airplane-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found airplanes, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of airplanes 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": "head-airplane-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new airplane.", "operationId": "post-airplane", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the airplane to create.", "content": { @@ -73,16 +124,16 @@ "description": "The airplane was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -103,6 +154,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -116,6 +180,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -137,12 +204,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -163,6 +246,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -193,12 +289,12 @@ "204": { "description": "The airplane 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." + }, "404": { "description": "The airplane or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -250,6 +346,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -263,6 +372,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -284,12 +396,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -312,6 +440,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -325,6 +466,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -346,12 +490,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -392,12 +552,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -438,12 +598,12 @@ "204": { "description": "The flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -484,12 +644,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -503,6 +663,21 @@ ], "summary": "Retrieves a collection of flight-attendants.", "operationId": "get-flight-attendant-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found flight-attendants, or an empty array if none were found.", @@ -513,6 +688,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -523,9 +701,27 @@ "summary": "Retrieves a collection of flight-attendants 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": "head-flight-attendant-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -535,6 +731,21 @@ ], "summary": "Creates a new flight-attendant.", "operationId": "post-flight-attendant", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the flight-attendant to create.", "content": { @@ -564,16 +775,16 @@ "description": "The flight-attendant was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -594,6 +805,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -607,6 +831,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -628,12 +855,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -654,6 +897,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -684,12 +940,12 @@ "204": { "description": "The flight-attendant 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." + }, "404": { "description": "The flight-attendant or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -741,6 +997,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -754,6 +1023,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -775,12 +1047,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -803,6 +1091,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -816,6 +1117,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -837,12 +1141,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -883,12 +1203,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -929,12 +1249,12 @@ "204": { "description": "The purser-on-flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -975,12 +1295,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1003,6 +1323,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1016,6 +1349,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1037,12 +1373,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1065,6 +1417,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1078,6 +1443,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1099,12 +1467,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1145,12 +1529,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1191,12 +1575,12 @@ "204": { "description": "The scheduled-for-flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1237,12 +1621,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1256,6 +1640,21 @@ ], "summary": "Retrieves a collection of flights.", "operationId": "get-flight-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found flights, or an empty array if none were found.", @@ -1266,6 +1665,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1276,9 +1678,27 @@ "summary": "Retrieves a collection of flights 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": "head-flight-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1288,6 +1708,21 @@ ], "summary": "Creates a new flight.", "operationId": "post-flight", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the flight to create.", "content": { @@ -1317,16 +1752,16 @@ "description": "The flight was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -1347,6 +1782,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1360,6 +1808,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1381,12 +1832,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1407,6 +1874,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -1437,12 +1917,12 @@ "204": { "description": "The flight 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." + }, "404": { "description": "The flight or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -1494,6 +1974,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1507,6 +2000,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1528,12 +2024,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1556,6 +2068,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1569,6 +2094,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1590,12 +2118,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1636,12 +2180,12 @@ "204": { "description": "The backup-purser relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1664,6 +2208,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1677,6 +2234,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1698,12 +2258,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1726,6 +2302,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1739,6 +2328,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1760,12 +2352,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1806,12 +2414,12 @@ "204": { "description": "The flight-attendants were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1852,12 +2460,12 @@ "204": { "description": "The cabin-crew-members relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1898,12 +2506,12 @@ "204": { "description": "The flight-attendants were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1926,6 +2534,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1939,6 +2560,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1960,12 +2584,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1988,6 +2628,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2001,6 +2654,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2022,12 +2678,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2068,12 +2740,12 @@ "204": { "description": "The passengers were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2114,12 +2786,12 @@ "204": { "description": "The passengers relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2160,12 +2832,12 @@ "204": { "description": "The passengers were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2188,6 +2860,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2201,6 +2886,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2222,12 +2910,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2250,6 +2954,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2263,6 +2980,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2284,12 +3004,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2330,12 +3066,12 @@ "204": { "description": "The purser relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2425,9 +3161,6 @@ "additionalProperties": false }, "airplane-attributes-in-response": { - "required": [ - "name" - ], "type": "object", "properties": { "name": { @@ -2501,7 +3234,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2613,7 +3349,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2680,7 +3419,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2781,10 +3523,6 @@ "additionalProperties": false }, "flight-attendant-attributes-in-response": { - "required": [ - "email-address", - "profile-image-url" - ], "type": "object", "properties": { "email-address": { @@ -2838,7 +3576,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2950,7 +3691,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3005,7 +3749,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3040,7 +3787,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3107,7 +3857,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3208,7 +3961,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3236,10 +3992,6 @@ "additionalProperties": false }, "flight-attributes-in-response": { - "required": [ - "final-destination", - "services-on-board" - ], "type": "object", "properties": { "final-destination": { @@ -3306,7 +4058,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3411,7 +4166,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3466,7 +4224,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3533,7 +4294,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3610,9 +4374,6 @@ "additionalProperties": false }, "flight-relationships-in-response": { - "required": [ - "purser" - ], "type": "object", "properties": { "cabin-crew-members": { @@ -3672,7 +4433,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3697,7 +4461,6 @@ }, "links-in-resource-collection-document": { "required": [ - "first", "self" ], "type": "object", @@ -3710,7 +4473,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -3743,7 +4505,6 @@ }, "links-in-resource-identifier-collection-document": { "required": [ - "first", "related", "self" ], @@ -3761,7 +4522,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -3841,7 +4601,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3877,7 +4640,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3922,7 +4688,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3973,7 +4742,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4013,7 +4785,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4068,7 +4843,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4115,7 +4893,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4156,7 +4937,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4197,7 +4981,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4240,7 +5027,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json index 148b2203bd..57e4d546ce 100644 --- a/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/CamelCase/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of supermarkets.", "operationId": "getSupermarketCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found supermarkets, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of supermarkets 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": "headSupermarketCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new supermarket.", "operationId": "postSupermarket", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the supermarket to create.", "content": { @@ -73,16 +124,16 @@ "description": "The supermarket was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The supermarket 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." + }, "404": { "description": "The supermarket or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The backupStoreManager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -430,6 +590,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -443,6 +616,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -465,12 +641,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -494,6 +686,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -507,6 +712,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -529,12 +737,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -576,12 +800,12 @@ "204": { "description": "The staffMembers were successfully added, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -623,12 +847,12 @@ "204": { "description": "The cashiers relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -670,12 +894,12 @@ "204": { "description": "The staffMembers were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -699,6 +923,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -712,6 +949,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -734,12 +974,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -763,6 +1019,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -776,6 +1045,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -798,12 +1070,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -845,12 +1133,12 @@ "204": { "description": "The storeManager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -880,7 +1168,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -905,7 +1196,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -918,7 +1208,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -951,7 +1240,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -969,7 +1257,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1049,7 +1336,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1085,7 +1375,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1130,15 +1423,15 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false }, "staffMemberAttributesInResponse": { - "required": [ - "name" - ], "type": "object", "properties": { "name": { @@ -1180,7 +1473,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1220,7 +1516,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1275,7 +1574,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1310,7 +1612,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1351,7 +1656,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1392,9 +1700,6 @@ "additionalProperties": false }, "supermarketAttributesInResponse": { - "required": [ - "nameOfCity" - ], "type": "object", "properties": { "nameOfCity": { @@ -1439,7 +1744,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1551,7 +1859,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1618,7 +1929,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1681,9 +1995,6 @@ "additionalProperties": false }, "supermarketRelationshipsInResponse": { - "required": [ - "storeManager" - ], "type": "object", "properties": { "storeManager": { @@ -1760,7 +2071,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1803,7 +2117,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json index d439357cae..97477c6b01 100644 --- a/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/KebabCase/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of supermarkets.", "operationId": "get-supermarket-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found supermarkets, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of supermarkets 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": "head-supermarket-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new supermarket.", "operationId": "post-supermarket", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the supermarket to create.", "content": { @@ -73,16 +124,16 @@ "description": "The supermarket was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The supermarket 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." + }, "404": { "description": "The supermarket or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The backup-store-manager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -430,6 +590,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -443,6 +616,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -465,12 +641,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -494,6 +686,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -507,6 +712,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -529,12 +737,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -576,12 +800,12 @@ "204": { "description": "The staff-members were successfully added, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -623,12 +847,12 @@ "204": { "description": "The cashiers relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -670,12 +894,12 @@ "204": { "description": "The staff-members were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -699,6 +923,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -712,6 +949,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -734,12 +974,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -763,6 +1019,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -776,6 +1045,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -798,12 +1070,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The supermarket does not exist." } @@ -845,12 +1133,12 @@ "204": { "description": "The store-manager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -880,7 +1168,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -905,7 +1196,6 @@ }, "links-in-resource-collection-document": { "required": [ - "first", "self" ], "type": "object", @@ -918,7 +1208,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -951,7 +1240,6 @@ }, "links-in-resource-identifier-collection-document": { "required": [ - "first", "related", "self" ], @@ -969,7 +1257,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1049,7 +1336,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1085,7 +1375,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1130,15 +1423,15 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false }, "staff-member-attributes-in-response": { - "required": [ - "name" - ], "type": "object", "properties": { "name": { @@ -1180,7 +1473,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1220,7 +1516,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1275,7 +1574,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1310,7 +1612,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1351,7 +1656,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1392,9 +1700,6 @@ "additionalProperties": false }, "supermarket-attributes-in-response": { - "required": [ - "name-of-city" - ], "type": "object", "properties": { "name-of-city": { @@ -1439,7 +1744,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1551,7 +1859,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1618,7 +1929,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1681,9 +1995,6 @@ "additionalProperties": false }, "supermarket-relationships-in-response": { - "required": [ - "store-manager" - ], "type": "object", "properties": { "store-manager": { @@ -1760,7 +2071,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1803,7 +2117,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json index ed70846149..22d5a1b77c 100644 --- a/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json +++ b/test/OpenApiClientTests/NamingConventions/PascalCase/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of Supermarkets.", "operationId": "GetSupermarketCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found Supermarkets, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of Supermarkets 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": "HeadSupermarketCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new Supermarket.", "operationId": "PostSupermarket", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the Supermarket to create.", "content": { @@ -73,16 +124,16 @@ "description": "The Supermarket was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The Supermarket 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." + }, "404": { "description": "The Supermarket or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The BackupStoreManager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The Supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The Supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -430,6 +590,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -443,6 +616,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -465,12 +641,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -494,6 +686,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -507,6 +712,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -529,12 +737,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -576,12 +800,12 @@ "204": { "description": "The StaffMembers were successfully added, which did not result in additional changes." }, - "404": { - "description": "The Supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The Supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -623,12 +847,12 @@ "204": { "description": "The Cashiers relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The Supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The Supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -670,12 +894,12 @@ "204": { "description": "The StaffMembers were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The Supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The Supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -699,6 +923,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -712,6 +949,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -734,12 +974,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -763,6 +1019,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -776,6 +1045,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -798,12 +1070,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The Supermarket does not exist." } @@ -845,12 +1133,12 @@ "204": { "description": "The StoreManager relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The Supermarket does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The Supermarket does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -880,7 +1168,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -905,7 +1196,6 @@ }, "LinksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -918,7 +1208,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -951,7 +1240,6 @@ }, "LinksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -969,7 +1257,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1049,7 +1336,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1085,7 +1375,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1130,15 +1423,15 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false }, "StaffMemberAttributesInResponse": { - "required": [ - "Name" - ], "type": "object", "properties": { "Name": { @@ -1180,7 +1473,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1220,7 +1516,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1275,7 +1574,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1310,7 +1612,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1351,7 +1656,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1392,9 +1700,6 @@ "additionalProperties": false }, "SupermarketAttributesInResponse": { - "required": [ - "NameOfCity" - ], "type": "object", "properties": { "NameOfCity": { @@ -1439,7 +1744,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1551,7 +1859,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1618,7 +1929,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1681,9 +1995,6 @@ "additionalProperties": false }, "SupermarketRelationshipsInResponse": { - "required": [ - "StoreManager" - ], "type": "object", "properties": { "StoreManager": { @@ -1760,7 +2071,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1803,7 +2117,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs index 684a961a1b..f2eada42fa 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/CreateResourceTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; @@ -50,7 +51,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -91,7 +92,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -132,7 +133,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -168,7 +169,7 @@ public async Task Can_clear_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -206,7 +207,7 @@ public async Task Cannot_clear_relationship(string relationshipPropertyName, str var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -242,7 +243,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs index baad3e94d6..bad0c97842 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/UpdateResourceTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; @@ -36,7 +37,7 @@ public async Task Cannot_omit_Id() var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -78,7 +79,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -119,7 +120,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json index 993604786b..8f40cea0b0 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOff/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of resources.", "operationId": "getResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found resources, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of resources 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": "headResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new resource.", "operationId": "postResource", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the resource to create.", "content": { @@ -73,16 +124,16 @@ "description": "The resource was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The resource 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." + }, "404": { "description": "The resource or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -448,12 +608,12 @@ "204": { "description": "The requiredToMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -495,12 +655,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -524,6 +684,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -537,6 +710,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -559,12 +735,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -588,6 +780,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -601,6 +806,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -623,12 +831,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -670,12 +894,12 @@ "204": { "description": "The requiredToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -699,6 +923,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -712,6 +949,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -734,12 +974,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -763,6 +1019,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -776,6 +1045,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -798,12 +1070,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -845,12 +1133,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -892,12 +1180,12 @@ "204": { "description": "The toMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -939,12 +1227,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -968,6 +1256,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -981,6 +1282,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1003,12 +1307,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1032,6 +1352,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1045,6 +1378,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1067,12 +1403,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1114,12 +1466,12 @@ "204": { "description": "The toOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1151,7 +1503,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1184,7 +1539,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1232,7 +1590,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1243,31 +1604,6 @@ ], "type": "string" }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" - } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, "linksInRelationshipObject": { "required": [ "related", @@ -1288,7 +1624,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -1301,7 +1636,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1334,7 +1668,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -1352,7 +1685,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1425,7 +1757,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1454,7 +1789,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1499,7 +1837,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1576,11 +1917,6 @@ "additionalProperties": false }, "resourceAttributesInResponse": { - "required": [ - "requiredNullableValueType", - "requiredReferenceType", - "requiredValueType" - ], "type": "object", "properties": { "referenceType": { @@ -1635,7 +1971,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1747,7 +2086,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1807,7 +2149,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1885,10 +2230,6 @@ "additionalProperties": false }, "resourceRelationshipsInResponse": { - "required": [ - "requiredToMany", - "requiredToOne" - ], "type": "object", "properties": { "toOne": { @@ -1964,7 +2305,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs index f6eb4c4f1e..e4565eb40c 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/CreateResourceTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; @@ -49,7 +50,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -91,7 +92,7 @@ public async Task Cannot_set_attribute_to_default_value(string attributeProperty using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -130,7 +131,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -170,7 +171,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -205,7 +206,7 @@ public async Task Can_clear_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -244,7 +245,7 @@ public async Task Cannot_clear_relationship(string relationshipPropertyName, str var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -281,7 +282,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -318,7 +319,7 @@ public async Task Cannot_omit_relationship(string relationshipPropertyName, stri var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs index 9d049df43b..5b430f0c07 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/UpdateResourceTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOff.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; @@ -36,7 +37,7 @@ public async Task Cannot_omit_Id() var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -78,7 +79,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -119,7 +120,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOffMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json index 3cd727b975..1758ce7985 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOff/ModelStateValidationOn/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of resources.", "operationId": "getResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found resources, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of resources 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": "headResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new resource.", "operationId": "postResource", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the resource to create.", "content": { @@ -73,16 +124,16 @@ "description": "The resource was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The resource 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." + }, "404": { "description": "The resource or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -448,12 +608,12 @@ "204": { "description": "The requiredToMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -495,12 +655,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -524,6 +684,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -537,6 +710,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -559,12 +735,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -588,6 +780,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -601,6 +806,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -623,12 +831,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -670,12 +894,12 @@ "204": { "description": "The requiredToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -699,6 +923,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -712,6 +949,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -734,12 +974,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -763,6 +1019,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -776,6 +1045,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -798,12 +1070,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -845,12 +1133,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -892,12 +1180,12 @@ "204": { "description": "The toMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -939,12 +1227,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -968,6 +1256,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -981,6 +1282,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1003,12 +1307,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1032,6 +1352,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1045,6 +1378,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1067,12 +1403,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1114,12 +1466,12 @@ "204": { "description": "The toOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1151,7 +1503,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1184,7 +1539,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1232,7 +1590,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1260,7 +1621,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1294,32 +1658,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" + "additionalProperties": { + "type": "object", + "nullable": true } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } } }, "additionalProperties": false @@ -1344,7 +1686,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -1357,7 +1698,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1390,7 +1730,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -1408,7 +1747,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1481,7 +1819,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1510,7 +1851,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1555,7 +1899,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1627,10 +1974,6 @@ "additionalProperties": false }, "resourceAttributesInResponse": { - "required": [ - "requiredNullableValueType", - "requiredReferenceType" - ], "type": "object", "properties": { "referenceType": { @@ -1683,7 +2026,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1795,7 +2141,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1855,7 +2204,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1932,9 +2284,6 @@ "additionalProperties": false }, "resourceRelationshipsInResponse": { - "required": [ - "requiredToOne" - ], "type": "object", "properties": { "toOne": { @@ -2010,7 +2359,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2053,7 +2405,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs index ec143cb9ee..1ca0771467 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/CreateResourceTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; @@ -52,7 +53,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -97,7 +98,7 @@ public async Task Cannot_set_attribute_to_default_value(string attributeProperty using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -139,7 +140,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -183,7 +184,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -221,7 +222,7 @@ public async Task Can_clear_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -263,7 +264,7 @@ public async Task Cannot_clear_relationship(string relationshipPropertyName, str var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -302,7 +303,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -341,7 +342,7 @@ public async Task Cannot_omit_relationship(string relationshipPropertyName, stri var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs index f17e9e08d3..96ed010cd4 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/UpdateResourceTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOff.GeneratedCode; using TestBuildingBlocks; @@ -38,7 +39,7 @@ public async Task Cannot_omit_Id() var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -84,7 +85,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -129,7 +130,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOffClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json index 8f117b6f84..e870fc7560 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOff/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of resources.", "operationId": "getResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found resources, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of resources 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": "headResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new resource.", "operationId": "postResource", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the resource to create.", "content": { @@ -73,16 +124,16 @@ "description": "The resource was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The resource 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." + }, "404": { "description": "The resource or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The nonNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -430,6 +590,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -443,6 +616,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -465,12 +641,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -494,6 +686,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -507,6 +712,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -529,12 +737,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -576,12 +800,12 @@ "204": { "description": "The nullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -605,6 +829,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -618,6 +855,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -640,12 +880,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -669,6 +925,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -682,6 +951,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -704,12 +976,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -751,12 +1039,12 @@ "204": { "description": "The requiredNonNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -780,6 +1068,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -793,6 +1094,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -815,12 +1119,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -844,6 +1164,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -857,6 +1190,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -879,12 +1215,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -926,12 +1278,12 @@ "204": { "description": "The requiredNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -955,6 +1307,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -968,6 +1333,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -990,12 +1358,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1019,6 +1403,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1032,6 +1429,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1054,12 +1454,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1101,12 +1517,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1148,12 +1564,12 @@ "204": { "description": "The requiredToMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1195,12 +1611,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1224,6 +1640,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1237,6 +1666,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1259,12 +1691,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1288,6 +1736,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1301,6 +1762,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1323,12 +1787,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1370,12 +1850,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1417,12 +1897,12 @@ "204": { "description": "The toMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1464,12 +1944,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1501,7 +1981,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1534,7 +2017,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1582,7 +2068,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1610,7 +2099,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1644,32 +2136,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" + "additionalProperties": { + "type": "object", + "nullable": true } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } } }, "additionalProperties": false @@ -1694,7 +2164,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -1707,7 +2176,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1740,7 +2208,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -1758,7 +2225,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1831,7 +2297,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1860,7 +2329,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1905,7 +2377,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1997,12 +2472,6 @@ "additionalProperties": false }, "resourceAttributesInResponse": { - "required": [ - "requiredNonNullableReferenceType", - "requiredNullableReferenceType", - "requiredNullableValueType", - "requiredValueType" - ], "type": "object", "properties": { "nonNullableReferenceType": { @@ -2064,7 +2533,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2176,7 +2648,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2236,7 +2711,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2343,11 +2821,6 @@ "additionalProperties": false }, "resourceRelationshipsInResponse": { - "required": [ - "requiredNonNullableToOne", - "requiredNullableToOne", - "requiredToMany" - ], "type": "object", "properties": { "nonNullableToOne": { @@ -2437,7 +2910,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2480,7 +2956,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs index b569c008ff..a9bab857af 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/CreateResourceTests.cs @@ -3,6 +3,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; @@ -51,7 +52,7 @@ public async Task Can_set_attribute_to_default_value(string attributePropertyNam using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -97,7 +98,7 @@ public async Task Cannot_set_attribute_to_default_value(string attributeProperty using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument, includeAttributeSelector); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -138,7 +139,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -182,7 +183,7 @@ public async Task Cannot_omit_attribute(string attributePropertyName, string jso using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -219,7 +220,7 @@ public async Task Can_clear_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -262,7 +263,7 @@ public async Task Cannot_clear_relationship(string relationshipPropertyName, str var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -301,7 +302,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PostResourceAsync(null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -342,7 +343,7 @@ public async Task Cannot_omit_relationship(string relationshipPropertyName, stri var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func> action = async () => await apiClient.PostResourceAsync(requestDocument); + Func> action = async () => await apiClient.PostResourceAsync(null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs index 11876a7a02..3fd8e55d06 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/UpdateResourceTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using FluentAssertions; using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; using OpenApiClientTests.ResourceFieldValidation.NullableReferenceTypesOn.ModelStateValidationOn.GeneratedCode; using TestBuildingBlocks; @@ -38,7 +39,7 @@ public async Task Cannot_omit_Id() var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, requestDocument); + Func action = async () => await apiClient.PatchResourceAsync(Unknown.TypedId.Int32, null, requestDocument); // Assert ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); @@ -84,7 +85,7 @@ public async Task Can_omit_attribute(string attributePropertyName, string jsonPr using IDisposable _ = apiClient.WithPartialAttributeSerialization(requestDocument); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); @@ -129,7 +130,7 @@ public async Task Can_omit_relationship(string relationshipPropertyName, string var apiClient = new NrtOnMsvOnClient(wrapper.HttpClient); // Act - await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), requestDocument)); + await ApiResponse.TranslateAsync(async () => await apiClient.PatchResourceAsync(int.Parse(requestDocument.Data.Id), null, requestDocument)); // Assert JsonElement document = wrapper.GetRequestBodyAsJson(); diff --git a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json index 7df69520b9..15c8c4bbc7 100644 --- a/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json +++ b/test/OpenApiClientTests/ResourceFieldValidation/NullableReferenceTypesOn/ModelStateValidationOn/swagger.g.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of resources.", "operationId": "getResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found resources, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of resources 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": "headResourceCollection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new resource.", "operationId": "postResource", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the resource to create.", "content": { @@ -73,16 +124,16 @@ "description": "The resource was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -104,6 +155,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -117,6 +181,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -139,12 +206,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -166,6 +249,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "requestBody": { @@ -196,12 +292,12 @@ "204": { "description": "The resource 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." + }, "404": { "description": "The resource or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -255,6 +351,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -268,6 +377,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -290,12 +402,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -319,6 +447,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -332,6 +473,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -354,12 +498,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -401,12 +561,12 @@ "204": { "description": "The nonNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -430,6 +590,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -443,6 +616,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -465,12 +641,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -494,6 +686,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -507,6 +712,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -529,12 +737,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -576,12 +800,12 @@ "204": { "description": "The nullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -605,6 +829,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -618,6 +855,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -640,12 +880,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -669,6 +925,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -682,6 +951,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -704,12 +976,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -751,12 +1039,12 @@ "204": { "description": "The requiredNonNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -780,6 +1068,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -793,6 +1094,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -815,12 +1119,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -844,6 +1164,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -857,6 +1190,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -879,12 +1215,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -926,12 +1278,12 @@ "204": { "description": "The requiredNullableToOne relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -955,6 +1307,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -968,6 +1333,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -990,12 +1358,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1019,6 +1403,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1032,6 +1429,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1054,12 +1454,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1101,12 +1517,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1148,12 +1564,12 @@ "204": { "description": "The requiredToMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1195,12 +1611,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1224,6 +1640,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1237,6 +1666,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1259,12 +1691,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1288,6 +1736,19 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { @@ -1301,6 +1762,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1323,12 +1787,28 @@ "type": "integer", "format": "int32" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The resource does not exist." } @@ -1370,12 +1850,12 @@ "204": { "description": "The empties were successfully added, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1417,12 +1897,12 @@ "204": { "description": "The toMany relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1464,12 +1944,12 @@ "204": { "description": "The empties were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The resource does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The resource does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1501,7 +1981,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1534,7 +2017,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1582,7 +2068,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1610,7 +2099,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1644,32 +2136,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } - } - }, - "additionalProperties": false - }, - "jsonapiObject": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "ext": { - "type": "array", - "items": { - "type": "string" + "additionalProperties": { + "type": "object", + "nullable": true } - }, - "profile": { - "type": "array", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "additionalProperties": { } } }, "additionalProperties": false @@ -1694,7 +2164,6 @@ }, "linksInResourceCollectionDocument": { "required": [ - "first", "self" ], "type": "object", @@ -1707,7 +2176,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1740,7 +2208,6 @@ }, "linksInResourceIdentifierCollectionDocument": { "required": [ - "first", "related", "self" ], @@ -1758,7 +2225,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -1831,7 +2297,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1860,7 +2329,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1905,7 +2377,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -1993,12 +2468,6 @@ "additionalProperties": false }, "resourceAttributesInResponse": { - "required": [ - "nonNullableReferenceType", - "requiredNonNullableReferenceType", - "requiredNullableReferenceType", - "requiredNullableValueType" - ], "type": "object", "properties": { "nonNullableReferenceType": { @@ -2058,7 +2527,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2170,7 +2642,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2230,7 +2705,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2337,11 +2815,6 @@ "additionalProperties": false }, "resourceRelationshipsInResponse": { - "required": [ - "nonNullableToOne", - "requiredNonNullableToOne", - "requiredNullableToOne" - ], "type": "object", "properties": { "nonNullableToOne": { @@ -2431,7 +2904,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2474,7 +2950,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj new file mode 100644 index 0000000000..fce4f3d735 --- /dev/null +++ b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj @@ -0,0 +1,31 @@ + + + $(TargetFrameworkName) + + + + + + + + + + + + + + + + + + + + + OpenApiEndToEndTests.QueryStrings.GeneratedCode + QueryStringsClient + QueryStringsClient.cs + NSwagCSharp + /UseBaseUrl:false /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + + + diff --git a/test/OpenApiEndToEndTests/QueryStrings/FilterTests.cs b/test/OpenApiEndToEndTests/QueryStrings/FilterTests.cs new file mode 100644 index 0000000000..f4ea0e477b --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/FilterTests.cs @@ -0,0 +1,153 @@ +using System.Net; +using FluentAssertions; +using JsonApiDotNetCore.OpenApi.Client.Exceptions; +using OpenApiEndToEndTests.QueryStrings.GeneratedCode; +using OpenApiTests; +using OpenApiTests.QueryStrings; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiEndToEndTests.QueryStrings; + +public sealed class FilterTests : IClassFixture, QueryStringsDbContext>> +{ + private readonly IntegrationTestContext, QueryStringsDbContext> _testContext; + private readonly ITestOutputHelper _testOutputHelper; + private readonly QueryStringFakers _fakers = new(); + + public FilterTests(IntegrationTestContext, QueryStringsDbContext> testContext, ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _testOutputHelper = testOutputHelper; + + testContext.UseController(); + } + + [Fact] + public async Task Can_filter_in_primary_resources() + { + // Arrange + List nodes = _fakers.Node.Generate(2); + nodes[0].Name = "John No Quote"; + nodes[1].Name = "Brian O'Quote"; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.AddRange(nodes); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["filter"] = "equals(name,'Brian O''Quote')" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeCollectionAsync(queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(nodes[1].StringId); + response.Data.ElementAt(0).Attributes.Name.Should().Be(nodes[1].Name); + response.Data.ElementAt(0).Attributes.Comment.Should().Be(nodes[1].Comment); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(1)); + } + + [Fact] + public async Task Can_filter_in_secondary_resources() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(2).ToHashSet(); + node.Children.ElementAt(0).Comment = "Discount: $10"; + node.Children.ElementAt(1).Comment = "Discount: 5%"; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["filter"] = "and(startsWith(comment,'Discount:'),contains(comment,'%'))" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeChildrenAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(1).StringId); + response.Data.ElementAt(0).Attributes.Name.Should().Be(node.Children.ElementAt(1).Name); + response.Data.ElementAt(0).Attributes.Comment.Should().Be(node.Children.ElementAt(1).Comment); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(1)); + } + + [Fact] + public async Task Can_filter_at_ToMany_relationship_endpoint() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(2).ToHashSet(); + node.Children.ElementAt(0).Children = _fakers.Node.Generate(1).ToHashSet(); + node.Children.ElementAt(1).Children = _fakers.Node.Generate(2).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["filter"] = "greaterThan(count(children),'1')" + }; + + // Act + NodeIdentifierCollectionResponseDocument response = await apiClient.GetNodeChildrenRelationshipAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(1).StringId); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(1)); + } + + [Fact] + public async Task Cannot_use_empty_filter() + { + // Arrange + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["filter"] = null + }; + + // Act + Func action = async () => _ = await apiClient.GetNodeAsync(1, queryString); + + // Assert + ApiException exception = (await action.Should().ThrowExactlyAsync()).Which; + exception.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); + exception.Message.Should().StartWith("The query string is invalid."); + exception.Message.Should().Contain("Missing value for 'filter' query string parameter."); + } +} diff --git a/test/OpenApiEndToEndTests/QueryStrings/GeneratedCode/QueryStringsClient.cs b/test/OpenApiEndToEndTests/QueryStrings/GeneratedCode/QueryStringsClient.cs new file mode 100644 index 0000000000..e0bd59a9a7 --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/GeneratedCode/QueryStringsClient.cs @@ -0,0 +1,62 @@ +using JsonApiDotNetCore.OpenApi.Client; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using TestBuildingBlocks; +using Xunit.Abstractions; + +// ReSharper disable UnusedParameterInPartialMethod + +namespace OpenApiEndToEndTests.QueryStrings.GeneratedCode; + +internal partial class QueryStringsClient : JsonApiClient +{ + private readonly ILogger? _logger; + + public QueryStringsClient(HttpClient httpClient, ITestOutputHelper testOutputHelper) + : this(httpClient, CreateLogger(testOutputHelper)) + { + } + + private QueryStringsClient(HttpClient httpClient, ILogger logger) + : this(httpClient) + { + _logger = logger; + } + + private static ILogger CreateLogger(ITestOutputHelper testOutputHelper) + { + var loggerFactory = new LoggerFactory(new[] + { + new XUnitLoggerProvider(testOutputHelper, LogOutputFields.Message) + }); + + return loggerFactory.CreateLogger(); + } + + partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + SetSerializerSettingsForJsonApi(settings); + + settings.Formatting = Formatting.Indented; + } + + partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string url) + { + if (_logger != null && _logger.IsEnabled(LogLevel.Debug)) + { + string? requestBody = request.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); + _logger.LogDebug(requestBody != null ? $"--> {request}{Environment.NewLine}{Environment.NewLine}{requestBody}" : $"--> {request}"); + } + } + + partial void ProcessResponse(HttpClient client, HttpResponseMessage response) + { + if (_logger != null && _logger.IsEnabled(LogLevel.Debug)) + { + string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + _logger.LogDebug( + !string.IsNullOrEmpty(responseBody) ? $"<-- {response}{Environment.NewLine}{Environment.NewLine}{responseBody}" : $"<-- {response}"); + } + } +} diff --git a/test/OpenApiEndToEndTests/QueryStrings/PaginationTests.cs b/test/OpenApiEndToEndTests/QueryStrings/PaginationTests.cs new file mode 100644 index 0000000000..43d2208e7b --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/PaginationTests.cs @@ -0,0 +1,169 @@ +using System.Net; +using FluentAssertions; +using JsonApiDotNetCore.OpenApi.Client.Exceptions; +using OpenApiEndToEndTests.QueryStrings.GeneratedCode; +using OpenApiTests; +using OpenApiTests.QueryStrings; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiEndToEndTests.QueryStrings; + +public sealed class PaginationTests : IClassFixture, QueryStringsDbContext>> +{ + private readonly IntegrationTestContext, QueryStringsDbContext> _testContext; + private readonly ITestOutputHelper _testOutputHelper; + private readonly QueryStringFakers _fakers = new(); + + public PaginationTests(IntegrationTestContext, QueryStringsDbContext> testContext, ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _testOutputHelper = testOutputHelper; + + testContext.UseController(); + } + + [Fact] + public async Task Can_paginate_in_primary_resources() + { + // Arrange + List nodes = _fakers.Node.Generate(3); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.AddRange(nodes); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["page[size]"] = "1", + ["page[number]"] = "2" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeCollectionAsync(queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(nodes[1].StringId); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(3)); + } + + [Fact] + public async Task Can_paginate_in_secondary_resources() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(3).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["page[size]"] = "2", + ["page[number]"] = "1" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeChildrenAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(2); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(0).StringId); + response.Data.ElementAt(1).Id.Should().Be(node.Children.ElementAt(1).StringId); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(3)); + } + + [Fact] + public async Task Can_paginate_at_ToMany_relationship_endpoint() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(3).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["page[size]"] = "2", + ["page[number]"] = "2" + }; + + // Act + NodeIdentifierCollectionResponseDocument response = await apiClient.GetNodeChildrenRelationshipAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(2).StringId); + response.Meta.ShouldNotBeNull(); + response.Meta.ShouldContainKey("total").With(total => total.Should().Be(3)); + } + + [Fact] + public async Task Cannot_use_empty_page_size() + { + // Arrange + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["page[size]"] = null + }; + + // Act + Func action = async () => _ = await apiClient.GetNodeAsync(1, queryString); + + // Assert + ApiException exception = (await action.Should().ThrowExactlyAsync()).Which; + exception.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); + exception.Message.Should().StartWith("The query string is invalid."); + exception.Message.Should().Contain("Missing value for 'page[size]' query string parameter."); + } + + [Fact] + public async Task Cannot_use_empty_page_number() + { + // Arrange + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["page[number]"] = null + }; + + // Act + Func action = async () => _ = await apiClient.GetNodeAsync(1, queryString); + + // Assert + ApiException exception = (await action.Should().ThrowExactlyAsync()).Which; + exception.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); + exception.Message.Should().StartWith("The query string is invalid."); + exception.Message.Should().Contain("Missing value for 'page[number]' query string parameter."); + } +} diff --git a/test/OpenApiEndToEndTests/QueryStrings/SortTests.cs b/test/OpenApiEndToEndTests/QueryStrings/SortTests.cs new file mode 100644 index 0000000000..57380a7c33 --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/SortTests.cs @@ -0,0 +1,146 @@ +using System.Net; +using FluentAssertions; +using JsonApiDotNetCore.OpenApi.Client.Exceptions; +using OpenApiEndToEndTests.QueryStrings.GeneratedCode; +using OpenApiTests; +using OpenApiTests.QueryStrings; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiEndToEndTests.QueryStrings; + +public sealed class SortTests : IClassFixture, QueryStringsDbContext>> +{ + private readonly IntegrationTestContext, QueryStringsDbContext> _testContext; + private readonly ITestOutputHelper _testOutputHelper; + private readonly QueryStringFakers _fakers = new(); + + public SortTests(IntegrationTestContext, QueryStringsDbContext> testContext, ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _testOutputHelper = testOutputHelper; + + testContext.UseController(); + } + + [Fact] + public async Task Can_sort_in_primary_resources() + { + // Arrange + List nodes = _fakers.Node.Generate(2); + nodes[0].Name = "A"; + nodes[1].Name = "B"; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.AddRange(nodes); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["sort"] = "-name" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeCollectionAsync(queryString); + + // Assert + response.Data.Should().HaveCount(2); + response.Data.ElementAt(0).Id.Should().Be(nodes[1].StringId); + response.Data.ElementAt(1).Id.Should().Be(nodes[0].StringId); + } + + [Fact] + public async Task Can_sort_in_secondary_resources() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(2).ToHashSet(); + node.Children.ElementAt(0).Name = "B"; + node.Children.ElementAt(1).Name = "A"; + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["sort"] = "name" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeChildrenAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(2); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(1).StringId); + response.Data.ElementAt(1).Id.Should().Be(node.Children.ElementAt(0).StringId); + } + + [Fact] + public async Task Can_sort_at_ToMany_relationship_endpoint() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(2).ToHashSet(); + node.Children.ElementAt(0).Children = _fakers.Node.Generate(1).ToHashSet(); + node.Children.ElementAt(1).Children = _fakers.Node.Generate(2).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["sort"] = "count(children)" + }; + + // Act + NodeIdentifierCollectionResponseDocument response = await apiClient.GetNodeChildrenRelationshipAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(2); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(0).StringId); + response.Data.ElementAt(1).Id.Should().Be(node.Children.ElementAt(1).StringId); + } + + [Fact] + public async Task Cannot_use_empty_sort() + { + // Arrange + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["sort"] = null + }; + + // Act + Func action = async () => _ = await apiClient.GetNodeAsync(1, queryString); + + // Assert + ApiException exception = (await action.Should().ThrowExactlyAsync()).Which; + exception.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); + exception.Message.Should().StartWith("The query string is invalid."); + exception.Message.Should().Contain("Missing value for 'sort' query string parameter."); + } +} diff --git a/test/OpenApiEndToEndTests/QueryStrings/SparseFieldSetTests.cs b/test/OpenApiEndToEndTests/QueryStrings/SparseFieldSetTests.cs new file mode 100644 index 0000000000..00d3962eeb --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/SparseFieldSetTests.cs @@ -0,0 +1,183 @@ +using FluentAssertions; +using OpenApiEndToEndTests.QueryStrings.GeneratedCode; +using OpenApiTests; +using OpenApiTests.QueryStrings; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiEndToEndTests.QueryStrings; + +public sealed class SparseFieldSetTests : IClassFixture, QueryStringsDbContext>> +{ + private readonly IntegrationTestContext, QueryStringsDbContext> _testContext; + private readonly ITestOutputHelper _testOutputHelper; + private readonly QueryStringFakers _fakers = new(); + + public SparseFieldSetTests(IntegrationTestContext, QueryStringsDbContext> testContext, + ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + _testOutputHelper = testOutputHelper; + + testContext.UseController(); + } + + [Fact] + public async Task Can_select_attribute_in_primary_resources() + { + // Arrange + Node node = _fakers.Node.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["fields[nodes]"] = "name" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeCollectionAsync(queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(node.StringId); + response.Data.ElementAt(0).Attributes.Name.Should().Be(node.Name); + response.Data.ElementAt(0).Attributes.Comment.Should().BeNull(); + response.Data.ElementAt(0).Relationships.Should().BeNull(); + } + + [Fact] + public async Task Can_select_fields_in_primary_resource() + { + // Arrange + Node node = _fakers.Node.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["fields[nodes]"] = "comment,parent" + }; + + // Act + NodePrimaryResponseDocument response = await apiClient.GetNodeAsync(node.Id, queryString); + + // Assert + response.Data.Id.Should().Be(node.StringId); + response.Data.Attributes.Name.Should().BeNull(); + response.Data.Attributes.Comment.Should().Be(node.Comment); + response.Data.Relationships.Parent.Should().NotBeNull(); + response.Data.Relationships.Children.Should().BeNull(); + } + + [Fact] + public async Task Can_select_fields_in_secondary_resources() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Children = _fakers.Node.Generate(1).ToHashSet(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + await dbContext.ClearTableAsync(); + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["fields[nodes]"] = "comment,children" + }; + + // Act + NodeCollectionResponseDocument response = await apiClient.GetNodeChildrenAsync(node.Id, queryString); + + // Assert + response.Data.Should().HaveCount(1); + response.Data.ElementAt(0).Id.Should().Be(node.Children.ElementAt(0).StringId); + response.Data.ElementAt(0).Attributes.Name.Should().BeNull(); + response.Data.ElementAt(0).Attributes.Comment.Should().Be(node.Children.ElementAt(0).Comment); + response.Data.ElementAt(0).Relationships.Parent.Should().BeNull(); + response.Data.ElementAt(0).Relationships.Children.Should().NotBeNull(); + } + + [Fact] + public async Task Can_select_fields_in_secondary_resource() + { + // Arrange + Node node = _fakers.Node.Generate(); + node.Parent = _fakers.Node.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["fields[nodes]"] = "comment,children" + }; + + // Act + NullableNodeSecondaryResponseDocument response = await apiClient.GetNodeParentAsync(node.Id, queryString); + + // Assert + response.Data.ShouldNotBeNull(); + response.Data.Id.Should().Be(node.Parent.StringId); + response.Data.Attributes.Name.Should().BeNull(); + response.Data.Attributes.Comment.Should().Be(node.Parent.Comment); + response.Data.Relationships.Parent.Should().BeNull(); + response.Data.Relationships.Children.Should().NotBeNull(); + } + + [Fact] + public async Task Can_select_empty_fieldset() + { + // Arrange + Node node = _fakers.Node.Generate(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + dbContext.Nodes.Add(node); + await dbContext.SaveChangesAsync(); + }); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + var apiClient = new QueryStringsClient(httpClient, _testOutputHelper); + + var queryString = new Dictionary + { + ["fields[nodes]"] = null + }; + + // Act + NodePrimaryResponseDocument response = await apiClient.GetNodeAsync(node.Id, queryString); + + // Assert + response.Data.Id.Should().Be(node.StringId); + response.Data.Attributes.Should().BeNull(); + } +} diff --git a/test/OpenApiEndToEndTests/QueryStrings/swagger.g.json b/test/OpenApiEndToEndTests/QueryStrings/swagger.g.json new file mode 100644 index 0000000000..94c4aab9b8 --- /dev/null +++ b/test/OpenApiEndToEndTests/QueryStrings/swagger.g.json @@ -0,0 +1,1566 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "paths": { + "/nodes": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves a collection of nodes.", + "operationId": "getNodeCollection", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found nodes, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves a collection of nodes 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": "headNodeCollection", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "post": { + "tags": [ + "nodes" + ], + "summary": "Creates a new node.", + "operationId": "postNode", + "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": null + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the node to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nodePostRequestDocument" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The node was successfully created, which resulted in additional changes. The newly created node is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The node 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." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." + }, + "409": { + "description": "A resource type in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." + } + } + } + }, + "/nodes/{id}": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves an individual node by its identifier.", + "operationId": "getNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves an individual node 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": "headNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Updates an existing node.", + "operationId": "patchNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to update.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the node to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nodePatchRequestDocument" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "The node was successfully updated, which resulted in additional changes. The updated node is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The node 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." + }, + "404": { + "description": "The node or a related resource does not exist." + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible." + }, + "422": { + "description": "Validation of the request body failed." + } + } + }, + "delete": { + "tags": [ + "nodes" + ], + "summary": "Deletes an existing node by its identifier.", + "operationId": "deleteNode", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to delete.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "204": { + "description": "The node was successfully deleted." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/nodes/{id}/children": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related nodes of an individual node's children relationship.", + "operationId": "getNodeChildren", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related nodes to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found nodes, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related nodes of an individual node's children 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": "headNodeChildren", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related nodes to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/nodes/{id}/relationships/children": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identities of an individual node's children relationship.", + "operationId": "getNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identities to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node identities, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nodeIdentifierCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identities of an individual node's children 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": "headNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identities to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "post": { + "tags": [ + "nodes" + ], + "summary": "Adds existing nodes to the children relationship of an individual node.", + "operationId": "postNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to add nodes to.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to add to the children relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The nodes were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Assigns existing nodes to the children relationship of an individual node.", + "operationId": "patchNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose children relationship to assign.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to assign to the children relationship, or an empty array to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The children relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + }, + "delete": { + "tags": [ + "nodes" + ], + "summary": "Removes existing nodes from the children relationship of an individual node.", + "operationId": "deleteNodeChildrenRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node to remove nodes from.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identities of the nodes to remove from the children relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The nodes were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + } + }, + "/nodes/{id}/parent": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node of an individual node's parent relationship.", + "operationId": "getNodeParent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node, or `null` if it was not found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableNodeSecondaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node of an individual node's parent 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": "headNodeParent", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + } + }, + "/nodes/{id}/relationships/parent": { + "get": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identity of an individual node's parent relationship.", + "operationId": "getNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identity to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found node identity, or `null` if it was not found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/nullableNodeIdentifierResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "head": { + "tags": [ + "nodes" + ], + "summary": "Retrieves the related node identity of an individual node's parent 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": "headNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose related node identity to retrieve.", + "required": true, + "schema": { + "type": "integer", + "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": null + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The node does not exist." + } + } + }, + "patch": { + "tags": [ + "nodes" + ], + "summary": "Clears or assigns an existing node to the parent relationship of an individual node.", + "operationId": "patchNodeParentRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the node whose parent relationship to assign or clear.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "The identity of the node to assign to the parent relationship, or `null` to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The parent relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed." + }, + "404": { + "description": "The node does not exist." + }, + "409": { + "description": "A resource type in the request body is incompatible." + } + } + } + } + }, + "components": { + "schemas": { + "linksInRelationshipObject": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceCollectionDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceDocument": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierCollectionDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceIdentifierDocument": { + "required": [ + "related", + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + } + }, + "additionalProperties": false + }, + "linksInResourceObject": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nodeAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeAttributesInPostRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "comment": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "nodeCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeDataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nodeDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInPatchRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInPostRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeDataInResponse": { + "required": [ + "id", + "links", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeAttributesInResponse" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeRelationshipsInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceObject" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nodeIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeResourceType" + } + ] + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "nodeIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nodePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInResponse" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInPostRequest": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInRequest" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "nodeRelationshipsInResponse": { + "type": "object", + "properties": { + "parent": { + "allOf": [ + { + "$ref": "#/components/schemas/nullableToOneNodeInResponse" + } + ] + }, + "children": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyNodeInResponse" + } + ] + } + }, + "additionalProperties": false + }, + "nodeResourceType": { + "enum": [ + "nodes" + ], + "type": "string" + }, + "nullableNodeIdentifierResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nullableNodeSecondaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeDataInResponse" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "nullableToOneNodeInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + "nullableToOneNodeInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/nodeIdentifier" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "toManyNodeInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyNodeInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationshipObject" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/nodeIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiTests/DocComments/DocCommentsStartup.cs b/test/OpenApiTests/DocComments/DocCommentsStartup.cs index 6049d701b1..d8c220e1a9 100644 --- a/test/OpenApiTests/DocComments/DocCommentsStartup.cs +++ b/test/OpenApiTests/DocComments/DocCommentsStartup.cs @@ -13,8 +13,9 @@ public sealed class DocCommentsStartup : OpenApiStartup { protected override void SetJsonApiOptions(JsonApiOptions options) { - options.ClientIdGeneration = ClientIdGenerationMode.Allowed; base.SetJsonApiOptions(options); + + options.ClientIdGeneration = ClientIdGenerationMode.Allowed; } protected override void SetupSwaggerGenAction(SwaggerGenOptions options) diff --git a/test/OpenApiTests/DocComments/DocCommentsTests.cs b/test/OpenApiTests/DocComments/DocCommentsTests.cs index 6d7962e47a..ae9788a0df 100644 --- a/test/OpenApiTests/DocComments/DocCommentsTests.cs +++ b/test/OpenApiTests/DocComments/DocCommentsTests.cs @@ -8,6 +8,9 @@ namespace OpenApiTests.DocComments; public sealed class DocCommentsTests : IClassFixture, DocCommentsDbContext>> { + private const string TextQueryString = + "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."; + private readonly OpenApiTestContext, DocCommentsDbContext> _testContext; public DocCommentsTests(OpenApiTestContext, DocCommentsDbContext> testContext) @@ -59,10 +62,18 @@ public async Task Endpoints_are_documented() { getElement.Should().HaveProperty("summary", "Retrieves a collection of skyscrapers."); + getElement.Should().ContainPath("parameters").With(parametersElement => + { + parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "query"); + parametersElement.Should().HaveProperty("[0].description", TextQueryString); + }); + getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(1); + responseElement.EnumerateObject().ShouldHaveCount(2); responseElement.Should().HaveProperty("200.description", "Successfully returns the found skyscrapers, or an empty array if none were found."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); }); }); @@ -71,10 +82,18 @@ public async Task Endpoints_are_documented() headElement.Should().HaveProperty("summary", "Retrieves a collection of skyscrapers without returning them."); headElement.Should().HaveProperty("description", "Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched."); + headElement.Should().ContainPath("parameters").With(parametersElement => + { + parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "query"); + parametersElement.Should().HaveProperty("[0].description", TextQueryString); + }); + headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(1); + responseElement.EnumerateObject().ShouldHaveCount(2); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); }); }); @@ -82,12 +101,19 @@ public async Task Endpoints_are_documented() { postElement.Should().HaveProperty("summary", "Creates a new skyscraper."); + postElement.Should().ContainPath("parameters").With(parametersElement => + { + parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "query"); + parametersElement.Should().HaveProperty("[0].description", TextQueryString); + }); + postElement.Should().ContainPath("responses").With(responseElement => { responseElement.EnumerateObject().ShouldHaveCount(5); responseElement.Should().HaveProperty("201.description", "The skyscraper was successfully created, which resulted in additional changes. The newly created skyscraper is returned."); responseElement.Should().HaveProperty("204.description", "The skyscraper was successfully created, which did not result in additional changes."); - responseElement.Should().HaveProperty("400.description", "The request body is missing or malformed."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid or the request body is missing or malformed."); responseElement.Should().HaveProperty("409.description", "A resource type in the request body is incompatible."); responseElement.Should().HaveProperty("422.description", "Validation of the request body failed."); }); @@ -102,14 +128,18 @@ public async Task Endpoints_are_documented() getElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "Successfully returns the found skyscraper."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -121,14 +151,18 @@ public async Task Endpoints_are_documented() headElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -139,8 +173,11 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to update."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); patchElement.Should().HaveProperty("requestBody.description", "The attributes and relationships of the skyscraper to update. Omitted fields are left unchanged."); @@ -151,7 +188,7 @@ public async Task Endpoints_are_documented() responseElement.Should().HaveProperty("200.description", "The skyscraper was successfully updated, which resulted in additional changes. The updated skyscraper is returned."); responseElement.Should().HaveProperty("204.description", "The skyscraper was successfully updated, which did not result in additional changes."); responseElement.Should().HaveProperty("404.description", "The skyscraper or a related resource does not exist."); - responseElement.Should().HaveProperty("400.description", "The request body is missing or malformed."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid or the request body is missing or malformed."); responseElement.Should().HaveProperty("409.description", "A resource type or identifier in the request body is incompatible."); responseElement.Should().HaveProperty("422.description", "Validation of the request body failed."); }); @@ -164,6 +201,7 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to delete."); }); @@ -184,14 +222,18 @@ public async Task Endpoints_are_documented() getElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related elevator to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "Successfully returns the found elevator, or `null` if it was not found."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -203,14 +245,18 @@ public async Task Endpoints_are_documented() headElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related elevator to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -224,14 +270,18 @@ public async Task Endpoints_are_documented() getElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related elevator identity to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "Successfully returns the found elevator identity, or `null` if it was not found."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -243,14 +293,18 @@ public async Task Endpoints_are_documented() headElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related elevator identity to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -262,6 +316,7 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose elevator relationship to assign or clear."); }); @@ -286,14 +341,18 @@ public async Task Endpoints_are_documented() getElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related spaces to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "Successfully returns the found spaces, or an empty array if none were found."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -305,14 +364,18 @@ public async Task Endpoints_are_documented() headElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related spaces to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -326,14 +389,18 @@ public async Task Endpoints_are_documented() getElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related space identities to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); getElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "Successfully returns the found space identities, or an empty array if none were found."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -345,14 +412,18 @@ public async Task Endpoints_are_documented() headElement.Should().ContainPath("parameters").With(parametersElement => { - parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.EnumerateArray().ShouldHaveCount(2); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose related space identities to retrieve."); + parametersElement.Should().HaveProperty("[1].in", "query"); + parametersElement.Should().HaveProperty("[1].description", TextQueryString); }); headElement.Should().ContainPath("responses").With(responseElement => { - responseElement.EnumerateObject().ShouldHaveCount(2); + responseElement.EnumerateObject().ShouldHaveCount(3); responseElement.Should().HaveProperty("200.description", "The operation completed successfully."); + responseElement.Should().HaveProperty("400.description", "The query string is invalid."); responseElement.Should().HaveProperty("404.description", "The skyscraper does not exist."); }); }); @@ -364,6 +435,7 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to add spaces to."); }); @@ -386,6 +458,7 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper whose spaces relationship to assign."); }); @@ -408,6 +481,7 @@ public async Task Endpoints_are_documented() patchElement.Should().ContainPath("parameters").With(parametersElement => { parametersElement.EnumerateArray().ShouldHaveCount(1); + parametersElement.Should().HaveProperty("[0].in", "path"); parametersElement.Should().HaveProperty("[0].description", "The identifier of the skyscraper to remove spaces from."); }); diff --git a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json index e95d8261b4..e014464a4c 100644 --- a/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json +++ b/test/OpenApiTests/LegacyOpenApiIntegration/swagger.json @@ -12,6 +12,21 @@ ], "summary": "Retrieves a collection of airplanes.", "operationId": "get-airplane-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found airplanes, or an empty array if none were found.", @@ -22,6 +37,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -32,9 +50,27 @@ "summary": "Retrieves a collection of airplanes 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": "head-airplane-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -44,6 +80,21 @@ ], "summary": "Creates a new airplane.", "operationId": "post-airplane", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the airplane to create.", "content": { @@ -73,16 +124,16 @@ "description": "The airplane was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -103,6 +154,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -116,6 +180,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -137,12 +204,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -163,6 +246,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -193,12 +289,12 @@ "204": { "description": "The airplane 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." + }, "404": { "description": "The airplane or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -250,6 +346,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -263,6 +372,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -284,12 +396,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -312,6 +440,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -325,6 +466,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -346,12 +490,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The airplane does not exist." } @@ -392,12 +552,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -438,12 +598,12 @@ "204": { "description": "The flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -484,12 +644,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The airplane does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The airplane does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -503,6 +663,21 @@ ], "summary": "Retrieves a collection of flight-attendants.", "operationId": "get-flight-attendant-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found flight-attendants, or an empty array if none were found.", @@ -513,6 +688,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -523,9 +701,27 @@ "summary": "Retrieves a collection of flight-attendants 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": "head-flight-attendant-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -535,6 +731,21 @@ ], "summary": "Creates a new flight-attendant.", "operationId": "post-flight-attendant", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the flight-attendant to create.", "content": { @@ -564,16 +775,16 @@ "description": "The flight-attendant was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -594,6 +805,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -607,6 +831,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -628,12 +855,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -654,6 +897,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -684,12 +940,12 @@ "204": { "description": "The flight-attendant 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." + }, "404": { "description": "The flight-attendant or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -741,6 +997,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -754,6 +1023,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -775,12 +1047,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -803,6 +1091,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -816,6 +1117,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -837,12 +1141,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -883,12 +1203,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -929,12 +1249,12 @@ "204": { "description": "The purser-on-flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -975,12 +1295,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1003,6 +1323,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1016,6 +1349,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1037,12 +1373,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1065,6 +1417,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1078,6 +1443,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1099,12 +1467,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight-attendant does not exist." } @@ -1145,12 +1529,12 @@ "204": { "description": "The flights were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1191,12 +1575,12 @@ "204": { "description": "The scheduled-for-flights relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1237,12 +1621,12 @@ "204": { "description": "The flights were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight-attendant does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight-attendant does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1256,6 +1640,21 @@ ], "summary": "Retrieves a collection of flights.", "operationId": "get-flight-collection", + "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": null + } + } + ], "responses": { "200": { "description": "Successfully returns the found flights, or an empty array if none were found.", @@ -1266,6 +1665,9 @@ } } } + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1276,9 +1678,27 @@ "summary": "Retrieves a collection of flights 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": "head-flight-collection", + "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": null + } + } + ], "responses": { "200": { "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." } } }, @@ -1288,6 +1708,21 @@ ], "summary": "Creates a new flight.", "operationId": "post-flight", + "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": null + } + } + ], "requestBody": { "description": "The attributes and relationships of the flight to create.", "content": { @@ -1317,16 +1752,16 @@ "description": "The flight was successfully created, which did not result in additional changes." }, "400": { - "description": "The request body is missing or malformed." + "description": "The query string is invalid or the request body is missing or malformed." + }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint." }, "409": { "description": "A resource type in the request body is incompatible." }, "422": { "description": "Validation of the request body failed." - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint." } } } @@ -1347,6 +1782,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1360,6 +1808,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1381,12 +1832,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1407,6 +1874,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "requestBody": { @@ -1437,12 +1917,12 @@ "204": { "description": "The flight 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." + }, "404": { "description": "The flight or a related resource does not exist." }, - "400": { - "description": "The request body is missing or malformed." - }, "409": { "description": "A resource type or identifier in the request body is incompatible." }, @@ -1494,6 +1974,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1507,6 +2000,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1528,12 +2024,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1556,6 +2068,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1569,6 +2094,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1590,12 +2118,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1636,12 +2180,12 @@ "204": { "description": "The backup-purser relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1664,6 +2208,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1677,6 +2234,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1698,12 +2258,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1726,6 +2302,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1739,6 +2328,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1760,12 +2352,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1806,12 +2414,12 @@ "204": { "description": "The flight-attendants were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1852,12 +2460,12 @@ "204": { "description": "The cabin-crew-members relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1898,12 +2506,12 @@ "204": { "description": "The flight-attendants were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -1926,6 +2534,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -1939,6 +2560,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1960,12 +2584,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -1988,6 +2628,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2001,6 +2654,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2022,12 +2678,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2068,12 +2740,12 @@ "204": { "description": "The passengers were successfully added, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2114,12 +2786,12 @@ "204": { "description": "The passengers relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2160,12 +2832,12 @@ "204": { "description": "The passengers were successfully removed, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2188,6 +2860,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2201,6 +2886,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2222,12 +2910,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2250,6 +2954,19 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { @@ -2263,6 +2980,9 @@ } } }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2284,12 +3004,28 @@ "schema": { "type": "string" } + }, + { + "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": null + } } ], "responses": { "200": { "description": "The operation completed successfully." }, + "400": { + "description": "The query string is invalid." + }, "404": { "description": "The flight does not exist." } @@ -2330,12 +3066,12 @@ "204": { "description": "The purser relationship was successfully updated, which did not result in additional changes." }, - "404": { - "description": "The flight does not exist." - }, "400": { "description": "The request body is missing or malformed." }, + "404": { + "description": "The flight does not exist." + }, "409": { "description": "A resource type in the request body is incompatible." } @@ -2425,9 +3161,6 @@ "additionalProperties": false }, "airplane-attributes-in-response": { - "required": [ - "name" - ], "type": "object", "properties": { "name": { @@ -2501,7 +3234,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2613,7 +3349,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2680,7 +3419,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2781,10 +3523,6 @@ "additionalProperties": false }, "flight-attendant-attributes-in-response": { - "required": [ - "email-address", - "profile-image-url" - ], "type": "object", "properties": { "email-address": { @@ -2838,7 +3576,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -2950,7 +3691,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3005,7 +3749,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3040,7 +3787,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3107,7 +3857,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3208,7 +3961,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3236,10 +3992,6 @@ "additionalProperties": false }, "flight-attributes-in-response": { - "required": [ - "final-destination", - "services-on-board" - ], "type": "object", "properties": { "final-destination": { @@ -3306,7 +4058,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3411,7 +4166,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3466,7 +4224,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3533,7 +4294,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3610,9 +4374,6 @@ "additionalProperties": false }, "flight-relationships-in-response": { - "required": [ - "purser" - ], "type": "object", "properties": { "cabin-crew-members": { @@ -3672,7 +4433,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3697,7 +4461,6 @@ }, "links-in-resource-collection-document": { "required": [ - "first", "self" ], "type": "object", @@ -3710,7 +4473,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -3743,7 +4505,6 @@ }, "links-in-resource-identifier-collection-document": { "required": [ - "first", "related", "self" ], @@ -3761,7 +4522,6 @@ "type": "string" }, "first": { - "minLength": 1, "type": "string" }, "last": { @@ -3841,7 +4601,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3877,7 +4640,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3922,7 +4688,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -3973,7 +4742,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4013,7 +4785,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4068,7 +4843,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4115,7 +4893,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4156,7 +4937,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4197,7 +4981,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false @@ -4240,7 +5027,10 @@ }, "meta": { "type": "object", - "additionalProperties": { } + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false diff --git a/test/OpenApiTests/OpenApiStartup.cs b/test/OpenApiTests/OpenApiStartup.cs index 6affd0bacc..e46efe63f0 100644 --- a/test/OpenApiTests/OpenApiStartup.cs +++ b/test/OpenApiTests/OpenApiStartup.cs @@ -20,6 +20,14 @@ public override void ConfigureServices(IServiceCollection services) services.AddOpenApi(mvcBuilder, SetupSwaggerGenAction); } + protected override void SetJsonApiOptions(JsonApiOptions options) + { + base.SetJsonApiOptions(options); + + options.UseRelativeLinks = true; + options.IncludeTotalResourceCount = true; + } + protected virtual void SetupSwaggerGenAction(SwaggerGenOptions options) { string documentationPath = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, ".xml"); diff --git a/test/OpenApiTests/QueryStrings/Node.cs b/test/OpenApiTests/QueryStrings/Node.cs new file mode 100644 index 0000000000..d1010b6197 --- /dev/null +++ b/test/OpenApiTests/QueryStrings/Node.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.QueryStrings; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.QueryStrings")] +public sealed class Node : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [Attr] + public string? Comment { get; set; } + + [HasOne] + public Node? Parent { get; set; } + + [HasMany] + public ISet Children { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/QueryStrings/QueryStringFakers.cs b/test/OpenApiTests/QueryStrings/QueryStringFakers.cs new file mode 100644 index 0000000000..26b9db2798 --- /dev/null +++ b/test/OpenApiTests/QueryStrings/QueryStringFakers.cs @@ -0,0 +1,19 @@ +using Bogus; +using JetBrains.Annotations; +using TestBuildingBlocks; + +// @formatter:wrap_chained_method_calls chop_if_long +// @formatter:wrap_before_first_method_call true + +namespace OpenApiTests.QueryStrings; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class QueryStringFakers : FakerContainer +{ + private readonly Lazy> _lazyNodeFaker = new(() => new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(node => node.Name, faker => faker.Lorem.Word()) + .RuleFor(node => node.Comment, faker => faker.Lorem.Sentence())); + + public Faker Node => _lazyNodeFaker.Value; +} diff --git a/test/OpenApiTests/QueryStrings/QueryStringTests.cs b/test/OpenApiTests/QueryStrings/QueryStringTests.cs new file mode 100644 index 0000000000..bc7dfb7a6b --- /dev/null +++ b/test/OpenApiTests/QueryStrings/QueryStringTests.cs @@ -0,0 +1,85 @@ +using System.Text.Json; +using FluentAssertions; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.QueryStrings; + +public sealed class QueryStringTests : IClassFixture, QueryStringsDbContext>> +{ + private readonly OpenApiTestContext, QueryStringsDbContext> _testContext; + + public QueryStringTests(OpenApiTestContext, QueryStringsDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.SwaggerDocumentOutputDirectory = "test/OpenApiEndToEndTests/QueryStrings"; + } + + [Theory] + [InlineData("/nodes.get")] + [InlineData("/nodes.head")] + [InlineData("/nodes.post")] + [InlineData("/nodes/{id}.get")] + [InlineData("/nodes/{id}.head")] + [InlineData("/nodes/{id}.patch")] + [InlineData("/nodes/{id}/parent.get")] + [InlineData("/nodes/{id}/parent.head")] + [InlineData("/nodes/{id}/relationships/parent.get")] + [InlineData("/nodes/{id}/relationships/parent.head")] + [InlineData("/nodes/{id}/children.get")] + [InlineData("/nodes/{id}/children.head")] + [InlineData("/nodes/{id}/relationships/children.get")] + [InlineData("/nodes/{id}/relationships/children.head")] + public async Task Endpoints_have_query_string_parameter(string endpointPath) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + document.Should().ContainPath($"paths.{endpointPath}").With(verbElement => + { + verbElement.Should().ContainPath("parameters").With(parametersElement => + { + parametersElement.EnumerateArray().Should().ContainSingle(element => element.GetProperty("in").ValueEquals("query")).Subject.With( + parameterElement => + { + parameterElement.Should().HaveProperty("name", "query"); + + parameterElement.Should().ContainPath("schema").With(schemaElement => + { + schemaElement.Should().HaveProperty("type", "object"); + + schemaElement.Should().ContainPath("additionalProperties").With(propertiesElement => + { + propertiesElement.Should().HaveProperty("type", "string"); + propertiesElement.Should().HaveProperty("nullable", true); + }); + + schemaElement.Should().HaveProperty("example", null); + }); + }); + }); + }); + } + + [Theory] + [InlineData("/nodes/{id}.delete")] + [InlineData("/nodes/{id}/relationships/parent.patch")] + [InlineData("/nodes/{id}/relationships/children.post")] + [InlineData("/nodes/{id}/relationships/children.patch")] + [InlineData("/nodes/{id}/relationships/children.delete")] + public async Task Endpoints_do_not_have_query_string_parameter(string endpointPath) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + document.Should().ContainPath($"paths.{endpointPath}").With(verbElement => + { + verbElement.Should().ContainPath("parameters").With(parametersElement => + { + parametersElement.EnumerateArray().Should().NotContain(element => element.GetProperty("in").ValueEquals("query")); + }); + }); + } +} diff --git a/test/OpenApiTests/QueryStrings/QueryStringsDbContext.cs b/test/OpenApiTests/QueryStrings/QueryStringsDbContext.cs new file mode 100644 index 0000000000..2be0dea6be --- /dev/null +++ b/test/OpenApiTests/QueryStrings/QueryStringsDbContext.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.QueryStrings; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class QueryStringsDbContext : TestableDbContext +{ + public DbSet Nodes => Set(); + + public QueryStringsDbContext(DbContextOptions options) + : base(options) + { + } +} diff --git a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs index e53117ff23..f71ca9d84b 100644 --- a/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs +++ b/test/TestBuildingBlocks/JsonElementAssertionExtensions.cs @@ -78,15 +78,15 @@ public void ContainProperty(string propertyName) .FailWith($"Expected JSON element '{escapedJson}' to contain a property named '{propertyName}'."); } - public JsonElement ContainPath(string path) + public JsonElement ContainPath(string jsonPath) { - Func elementSelector = () => _subject.SelectToken(path, true)!.Value; + Func elementSelector = () => _subject.SelectToken(jsonPath, true)!.Value; return elementSelector.Should().NotThrow().Subject; } - public void NotContainPath(string path) + public void NotContainPath(string jsonPath) { - JsonElement? pathToken = _subject.SelectToken(path); + JsonElement? pathToken = _subject.SelectToken(jsonPath); pathToken.Should().BeNull(); } @@ -116,9 +116,9 @@ public void Be(object? value) } } - public void HaveProperty(string propertyName, string propertyValue) + public void HaveProperty(string jsonPath, object? propertyValue) { - _subject.Should().ContainPath(propertyName).With(element => element.Should().Be(propertyValue)); + _subject.Should().ContainPath(jsonPath).With(element => element.Should().Be(propertyValue)); } public void ContainArrayElement(string value)