From 57855530418786f555643cd03f726896b40d4e1a Mon Sep 17 00:00:00 2001 From: verdie-g Date: Thu, 22 Feb 2024 23:28:19 -0500 Subject: [PATCH 1/6] openapi: support client-generated id --- .../ClientGeneratedId/PostTests.cs | 133 ++ .../ClientGeneratedId/swagger.g.json | 2078 +++++++++++++++++ .../OpenApiEndToEndTests.csproj | 7 + .../ClientGeneratedIdDbContext.cs | 12 + .../ClientGeneratedIdFakers.cs | 24 + .../ClientGeneratedIdTests.cs | 56 + test/OpenApiTests/ClientGeneratedId/Game.cs | 17 + test/OpenApiTests/ClientGeneratedId/Player.cs | 17 + 8 files changed, 2344 insertions(+) create mode 100644 test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs create mode 100644 test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json create mode 100644 test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs create mode 100644 test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs create mode 100644 test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs create mode 100644 test/OpenApiTests/ClientGeneratedId/Game.cs create mode 100644 test/OpenApiTests/ClientGeneratedId/Player.cs diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs b/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs new file mode 100644 index 0000000000..7b4a703978 --- /dev/null +++ b/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs @@ -0,0 +1,133 @@ +using FluentAssertions; +using OpenApiEndToEndTests.ClientGeneratedId.GeneratedCode; +using OpenApiTests; +using OpenApiTests.ClientGeneratedId; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiEndToEndTests.ClientGeneratedId; + +public sealed class PostTests : IClassFixture, ClientGeneratedIdDbContext>> +{ + private readonly IntegrationTestContext, ClientGeneratedIdDbContext> _testContext; + private readonly ClientGeneratedIdFakers _fakers = new(); + + public PostTests(IntegrationTestContext, ClientGeneratedIdDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + } + + [Fact] + public async Task Returns_error_if_required_id_is_omitted() + { + // Arrange + Player player = _fakers.Player.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + ClientGeneratedIdClient apiClient = new(httpClient); + + // Act + Func> action = () => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + { + Data = new PlayerDataInPostRequest + { + Id = null!, // FIXME: passing "" here works fine 🤔 + Attributes = new PlayerAttributesInPostRequest + { + Name = player.Name + } + } + }); + + // Assert + var exception = (await action.Should().ThrowAsync()).Subject.First(); + // Exception is Newtonsoft.Json.JsonSerializationException: Cannot write a null value for property 'id'. Property requires a value. Path 'data'. + // Probably not what we want. + } + + [Fact] + public async Task Requires_passing_id() + { + // Arrange + Player player = _fakers.Player.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + ClientGeneratedIdClient apiClient = new(httpClient); + + // Act + Func> action = () => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + { + Data = new PlayerDataInPostRequest + { + Id = player.StringId!, + Attributes = new PlayerAttributesInPostRequest + { + Name = player.Name + } + } + }); + + // Assert + PlayerPrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; + doc.Data.Id.Should().Be(player.StringId); + } + + [Fact] + public async Task Allows_passing_id() + { + // Arrange + Game game = _fakers.Game.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + ClientGeneratedIdClient apiClient = new(httpClient); + + // Act + Func> action = () => apiClient.PostGameAsync(null, new GamePostRequestDocument + { + Data = new GameDataInPostRequest + { + Id = game.StringId!, // FIXME: StringId is null, how to generate an id? + Attributes = new GameAttributesInPostRequest + { + Name = game.Name, + Price = (double)game.Price + } + } + }); + + // Assert + GamePrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; + doc.Data.Id.Should().Be(game.StringId); + } + + [Fact] + public async Task Allow_omitting_id() + { + // Arrange + Game game = _fakers.Game.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + ClientGeneratedIdClient apiClient = new(httpClient); + + // Act + Func> action = () => apiClient.PostGameAsync(null, new GamePostRequestDocument + { + Data = new GameDataInPostRequest + { + Id = null!, // FIXME: incorrect nullability here + Attributes = new GameAttributesInPostRequest + { + Name = game.Name, + Price = (double)game.Price + } + } + }); + + // Assert + GamePrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; + doc.Data.Id.Should().NotBeNullOrEmpty(); + } +} \ No newline at end of file diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json b/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json new file mode 100644 index 0000000000..41a2e3926a --- /dev/null +++ b/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json @@ -0,0 +1,2078 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenApiTests", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost" + } + ], + "paths": { + "/games": { + "get": { + "tags": [ + "games" + ], + "summary": "Retrieves a collection of games.", + "operationId": "getGameCollection", + "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": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found games, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gameCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "games" + ], + "summary": "Retrieves a collection of games 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": "headGameCollection", + "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": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "post": { + "tags": [ + "games" + ], + "summary": "Creates a new game.", + "operationId": "postGame", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the game to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/gamePostRequestDocument" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The game was successfully created, which resulted in additional changes. The newly created game is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gamePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The game was successfully created, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/games/{id}": { + "get": { + "tags": [ + "games" + ], + "summary": "Retrieves an individual game by its identifier.", + "operationId": "getGame", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the game to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found game.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gamePrimaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The game does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "games" + ], + "summary": "Retrieves an individual game 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": "headGame", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the game to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The game does not exist." + } + } + }, + "patch": { + "tags": [ + "games" + ], + "summary": "Updates an existing game.", + "operationId": "patchGame", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the game to update.", + "required": true, + "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": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the game to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/gamePatchRequestDocument" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "The game was successfully updated, which resulted in additional changes. The updated game is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gamePrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The game was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The game or a related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "games" + ], + "summary": "Deletes an existing game by its identifier.", + "operationId": "deleteGame", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the game to delete.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The game was successfully deleted." + }, + "404": { + "description": "The game does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves a collection of players.", + "operationId": "getPlayerCollection", + "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": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found players, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves a collection of players 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": "headPlayerCollection", + "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": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + } + } + }, + "post": { + "tags": [ + "players" + ], + "summary": "Creates a new player.", + "operationId": "postPlayer", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the player to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/playerPostRequestDocument" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The player was successfully created, which resulted in additional changes. The newly created player is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The player was successfully created, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players/{id}": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves an individual player by its identifier.", + "operationId": "getPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found player.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves an individual player 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": "headPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + }, + "patch": { + "tags": [ + "players" + ], + "summary": "Updates an existing player.", + "operationId": "patchPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to update.", + "required": true, + "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": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the player to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/playerPatchRequestDocument" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "The player was successfully updated, which resulted in additional changes. The updated player is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The player was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player or a related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "players" + ], + "summary": "Deletes an existing player by its identifier.", + "operationId": "deletePlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to delete.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The player was successfully deleted." + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players/{id}/games": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related games of an individual player's games relationship.", + "operationId": "getPlayerGames", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related games to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found games, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gameCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related games of an individual player's games 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": "headPlayerGames", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related games to retrieve.", + "required": true, + "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": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + } + }, + "/players/{id}/relationships/games": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related game identities of an individual player's games relationship.", + "operationId": "getPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related game identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found game identities, or an empty array if none were found.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related game identities of an individual player's games 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": "headPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related game identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully." + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + }, + "post": { + "tags": [ + "players" + ], + "summary": "Adds existing games to the games relationship of an individual player.", + "operationId": "postPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to add games to.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to add to the games relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "players" + ], + "summary": "Assigns existing games to the games relationship of an individual player.", + "operationId": "patchPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose games relationship to assign.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to assign to the games relationship, or an empty array to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "players" + ], + "summary": "Removes existing games from the games relationship of an individual player.", + "operationId": "deletePlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to remove games from.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to remove from the games relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "dataInResponse": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "type", + "mapping": { + "games": "#/components/schemas/gameDataInResponse", + "players": "#/components/schemas/playerDataInResponse" + } + }, + "x-abstract": true + }, + "errorLinks": { + "type": "object", + "properties": { + "about": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorObject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorLinks" + } + ], + "nullable": true + }, + "status": { + "type": "string" + }, + "code": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/errorSource" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { }, + "nullable": true + } + }, + "additionalProperties": false + }, + "errorResponseDocument": { + "required": [ + "errors" + ], + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/errorObject" + } + } + }, + "additionalProperties": false + }, + "errorSource": { + "type": "object", + "properties": { + "pointer": { + "type": "string", + "nullable": true + }, + "parameter": { + "type": "string", + "nullable": true + }, + "header": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "gameAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameAttributesInPostRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameDataInResponse" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "gameDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gameDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gameDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "required": [ + "links" + ], + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceData" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + ], + "additionalProperties": false + }, + "gameIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "gameIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "gamePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gamePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gamePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInResponse" + } + ] + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "gameResourceType": { + "enum": [ + "games" + ], + "type": "string", + "additionalProperties": false + }, + "linksInRelationship": { + "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 + }, + "linksInResourceData": { + "required": [ + "self" + ], + "type": "object", + "properties": { + "self": { + "minLength": 1, + "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 + }, + "playerAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerAttributesInPostRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/playerDataInResponse" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "playerDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/playerResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/playerAttributesInPatchRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/playerRelationshipsInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerDataInPostRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/playerResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/playerAttributesInPostRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/playerRelationshipsInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "required": [ + "links" + ], + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/playerAttributesInResponse" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/playerRelationshipsInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceData" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + ], + "additionalProperties": false + }, + "playerPatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/playerDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerPostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/playerDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerPrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/playerDataInResponse" + } + ] + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "playerRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "games": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerRelationshipsInPostRequest": { + "type": "object", + "properties": { + "games": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "playerRelationshipsInResponse": { + "type": "object", + "properties": { + "games": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInResponse" + } + ] + } + }, + "additionalProperties": false + }, + "playerResourceType": { + "enum": [ + "players" + ], + "type": "string", + "additionalProperties": false + }, + "toManyGameInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyGameInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationship" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj index 6566c17b4b..c6381e9737 100644 --- a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj +++ b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj @@ -23,6 +23,13 @@ + + OpenApiEndToEndTests.ClientGeneratedId.GeneratedCode + ClientGeneratedIdClient + ClientGeneratedIdClient.cs + NSwagCSharp + /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + OpenApiEndToEndTests.Headers.GeneratedCode HeadersClient diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs new file mode 100644 index 0000000000..a7faee6027 --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ClientGeneratedId; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ClientGeneratedIdDbContext(DbContextOptions options) : TestableDbContext(options) +{ + public DbSet Players => Set(); + public DbSet Games => Set(); +} diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs new file mode 100644 index 0000000000..3f4cf18b9a --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs @@ -0,0 +1,24 @@ +using Bogus; +using JetBrains.Annotations; +using TestBuildingBlocks; + +// @formatter:wrap_chained_method_calls chop_if_long +// @formatter:wrap_before_first_method_call true + +namespace OpenApiTests.ClientGeneratedId; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ClientGeneratedIdFakers : FakerContainer +{ + private readonly Lazy> _lazyPlayerFaker = new(() => new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(player => player.Name, faker => faker.Person.UserName)); + + private readonly Lazy> _lazyGameFaker = new(() => new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(player => player.Name, faker => faker.Commerce.ProductName()) + .RuleFor(player => player.Price, faker => decimal.Parse(faker.Commerce.Price()))); + + public Faker Player => _lazyPlayerFaker.Value; + public Faker Game => _lazyGameFaker.Value; +} diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs new file mode 100644 index 0000000000..7c6e03f822 --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs @@ -0,0 +1,56 @@ +using System.Text.Json; +using TestBuildingBlocks; +using Xunit; + +namespace OpenApiTests.ClientGeneratedId; + +public sealed class ClientGeneratedIdTests : IClassFixture, ClientGeneratedIdDbContext>> +{ + private readonly OpenApiTestContext, ClientGeneratedIdDbContext> _testContext; + + public ClientGeneratedIdTests(OpenApiTestContext, ClientGeneratedIdDbContext> testContext) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + + testContext.SwaggerDocumentOutputDirectory = "test/OpenApiEndToEndTests/ClientGeneratedId"; + } + + [Fact] + public async Task Post_data_should_have_required_id() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.playerDataInPostRequest").With(resourceDataInPostRequestElement => + { + resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + { + requiredElement.Should().ContainArrayElement("id"); + }); + + resourceDataInPostRequestElement.Should().ContainPath("properties.id"); + }); + } + + [Fact] + public async Task Post_data_should_have_non_required_id() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.gameDataInPostRequest").With(resourceDataInPostRequestElement => + { + resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + { + requiredElement.Should().NotContainArrayElement("id"); + }); + + resourceDataInPostRequestElement.Should().ContainPath("properties.id"); + }); + } +} diff --git a/test/OpenApiTests/ClientGeneratedId/Game.cs b/test/OpenApiTests/ClientGeneratedId/Game.cs new file mode 100644 index 0000000000..18e9574520 --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/Game.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ClientGeneratedId; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Allowed)] +public sealed class Game : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [Attr] + public decimal Price { get; set; } +} diff --git a/test/OpenApiTests/ClientGeneratedId/Player.cs b/test/OpenApiTests/ClientGeneratedId/Player.cs new file mode 100644 index 0000000000..5aa9d39132 --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/Player.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ClientGeneratedId; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Required)] +public sealed class Player : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [HasMany] + public List Games { get; set; } = []; +} From 5cabf90aac0194fc7df683eaefcd6e37481881d5 Mon Sep 17 00:00:00 2001 From: verdie-g Date: Fri, 23 Feb 2024 16:35:31 -0500 Subject: [PATCH 2/6] Complete tests --- .../ClientGeneratedId/PostTests.cs | 81 +- .../ClientGeneratedId/swagger.g.json | 3042 +++++++++++++++-- .../OpenApiEndToEndTests.csproj | 2 +- .../ClientGeneratedIdDbContext.cs | 1 + .../ClientGeneratedIdFakers.cs | 5 + .../ClientGeneratedIdTests.cs | 23 +- test/OpenApiTests/ClientGeneratedId/Game.cs | 2 +- test/OpenApiTests/ClientGeneratedId/Group.cs | 17 + test/OpenApiTests/ClientGeneratedId/Player.cs | 5 +- 9 files changed, 2903 insertions(+), 275 deletions(-) create mode 100644 test/OpenApiTests/ClientGeneratedId/Group.cs diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs b/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs index 7b4a703978..aad7e9da25 100644 --- a/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs +++ b/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs @@ -1,4 +1,7 @@ using FluentAssertions; +using FluentAssertions.Specialized; +using JsonApiDotNetCore.OpenApi.Client; +using Newtonsoft.Json; using OpenApiEndToEndTests.ClientGeneratedId.GeneratedCode; using OpenApiTests; using OpenApiTests.ClientGeneratedId; @@ -18,10 +21,11 @@ public PostTests(IntegrationTestContext(); testContext.UseController(); + testContext.UseController(); } [Fact] - public async Task Returns_error_if_required_id_is_omitted() + public async Task Omit_required_id() { // Arrange Player player = _fakers.Player.Generate(); @@ -30,35 +34,35 @@ public async Task Returns_error_if_required_id_is_omitted() ClientGeneratedIdClient apiClient = new(httpClient); // Act - Func> action = () => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument { Data = new PlayerDataInPostRequest { - Id = null!, // FIXME: passing "" here works fine 🤔 + Id = null!, Attributes = new PlayerAttributesInPostRequest { Name = player.Name } } - }); + })); // Assert - var exception = (await action.Should().ThrowAsync()).Subject.First(); - // Exception is Newtonsoft.Json.JsonSerializationException: Cannot write a null value for property 'id'. Property requires a value. Path 'data'. - // Probably not what we want. + ExceptionAssertions assertion = await action.Should().ThrowExactlyAsync(); + assertion.Which.Message.Should().Be("Cannot write a null value for property 'id'. Property requires a value. Path 'data'."); } [Fact] - public async Task Requires_passing_id() + public async Task Pass_required_id() { // Arrange Player player = _fakers.Player.Generate(); + player.Id = Guid.NewGuid(); using HttpClient httpClient = _testContext.Factory.CreateClient(); ClientGeneratedIdClient apiClient = new(httpClient); // Act - Func> action = () => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument { Data = new PlayerDataInPostRequest { @@ -68,15 +72,15 @@ public async Task Requires_passing_id() Name = player.Name } } - }); + })); // Assert - PlayerPrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; - doc.Data.Id.Should().Be(player.StringId); + PlayerPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; + doc.Should().BeNull(); } [Fact] - public async Task Allows_passing_id() + public async Task Omit_allowed_id() { // Arrange Game game = _fakers.Game.Generate(); @@ -85,49 +89,76 @@ public async Task Allows_passing_id() ClientGeneratedIdClient apiClient = new(httpClient); // Act - Func> action = () => apiClient.PostGameAsync(null, new GamePostRequestDocument + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { - Id = game.StringId!, // FIXME: StringId is null, how to generate an id? + Id = null!, Attributes = new GameAttributesInPostRequest { Name = game.Name, Price = (double)game.Price } } - }); + })); // Assert - GamePrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; - doc.Data.Id.Should().Be(game.StringId); + GamePrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; + doc?.Data.Id.Should().NotBeNullOrEmpty(); } [Fact] - public async Task Allow_omitting_id() + public async Task Pass_allowed_id() { // Arrange Game game = _fakers.Game.Generate(); + game.Id = Guid.NewGuid(); using HttpClient httpClient = _testContext.Factory.CreateClient(); ClientGeneratedIdClient apiClient = new(httpClient); // Act - Func> action = () => apiClient.PostGameAsync(null, new GamePostRequestDocument + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { - Id = null!, // FIXME: incorrect nullability here + Id = game.StringId!, Attributes = new GameAttributesInPostRequest { Name = game.Name, Price = (double)game.Price } } - }); + })); // Assert - GamePrimaryResponseDocument doc = (await action.Should().NotThrowAsync()).Subject; - doc.Data.Id.Should().NotBeNullOrEmpty(); + GamePrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; + doc.Should().BeNull(); } -} \ No newline at end of file + + [Fact] + public async Task Omit_forbidden_id() + { + // Arrange + Group group = _fakers.Group.Generate(); + + using HttpClient httpClient = _testContext.Factory.CreateClient(); + ClientGeneratedIdClient apiClient = new(httpClient); + + // Act + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGroupAsync(null, new GroupPostRequestDocument + { + Data = new GroupDataInPostRequest + { + Attributes = new GroupAttributesInPostRequest + { + Name = group.Name + } + } + })); + + // Assert + GroupPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; + doc?.Data.Id.Should().NotBeNullOrEmpty(); + } +} diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json b/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json index 41a2e3926a..e1f5bbcf8c 100644 --- a/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json +++ b/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json @@ -30,11 +30,28 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { "description": "Successfully returns the found games, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { @@ -43,6 +60,18 @@ } } }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, "400": { "description": "The query string is invalid.", "content": { @@ -75,11 +104,48 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." @@ -124,6 +190,16 @@ "responses": { "201": { "description": "The game was successfully created, which resulted in additional changes. The newly created game is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created game can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, "content": { "application/vnd.api+json": { "schema": { @@ -197,11 +273,28 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { "description": "Successfully returns the found game.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { @@ -210,6 +303,18 @@ } } }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, "400": { "description": "The query string is invalid.", "content": { @@ -261,11 +366,48 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." @@ -409,13 +551,13 @@ } } }, - "/players": { + "/groups": { "get": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves a collection of players.", - "operationId": "getPlayerCollection", + "summary": "Retrieves a collection of groups.", + "operationId": "getGroupCollection", "parameters": [ { "name": "query", @@ -429,15 +571,44 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "Successfully returns the found players, or an empty array if none were found.", + "description": "Successfully returns the found groups, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/playerCollectionResponseDocument" + "$ref": "#/components/schemas/groupCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" } } } @@ -456,11 +627,11 @@ }, "head": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves a collection of players without returning them.", + "summary": "Retrieves a collection of groups 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": "headPlayerCollection", + "operationId": "headGroupCollection", "parameters": [ { "name": "query", @@ -474,11 +645,48 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." @@ -487,10 +695,10 @@ }, "post": { "tags": [ - "players" + "groups" ], - "summary": "Creates a new player.", - "operationId": "postPlayer", + "summary": "Creates a new group.", + "operationId": "postGroup", "parameters": [ { "name": "query", @@ -507,13 +715,13 @@ } ], "requestBody": { - "description": "The attributes and relationships of the player to create.", + "description": "The attributes and relationships of the group to create.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/playerPostRequestDocument" + "$ref": "#/components/schemas/groupPostRequestDocument" } ] } @@ -522,17 +730,27 @@ }, "responses": { "201": { - "description": "The player was successfully created, which resulted in additional changes. The newly created player is returned.", + "description": "The group was successfully created, which resulted in additional changes. The newly created group is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created group can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" + "$ref": "#/components/schemas/groupPrimaryResponseDocument" } } } }, "204": { - "description": "The player was successfully created, which did not result in additional changes." + "description": "The group 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.", @@ -544,6 +762,16 @@ } } }, + "403": { + "description": "Client-generated IDs cannot be used at this endpoint.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, "409": { "description": "A resource type in the request body is incompatible.", "content": { @@ -567,18 +795,18 @@ } } }, - "/players/{id}": { + "/groups/{id}": { "get": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves an individual player by its identifier.", - "operationId": "getPlayer", + "summary": "Retrieves an individual group by its identifier.", + "operationId": "getGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to retrieve.", + "description": "The identifier of the group to retrieve.", "required": true, "schema": { "type": "string" @@ -596,15 +824,44 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "Successfully returns the found player.", + "description": "Successfully returns the found group.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" + "$ref": "#/components/schemas/groupPrimaryResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" } } } @@ -620,7 +877,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -633,16 +890,16 @@ }, "head": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves an individual player by its identifier without returning it.", + "summary": "Retrieves an individual group 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": "headPlayer", + "operationId": "headGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to retrieve.", + "description": "The identifier of the group to retrieve.", "required": true, "schema": { "type": "string" @@ -660,31 +917,68 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." }, "404": { - "description": "The player does not exist." + "description": "The group does not exist." } } }, "patch": { "tags": [ - "players" + "groups" ], - "summary": "Updates an existing player.", - "operationId": "patchPlayer", + "summary": "Updates an existing group.", + "operationId": "patchGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to update.", + "description": "The identifier of the group to update.", "required": true, "schema": { "type": "string" @@ -705,13 +999,13 @@ } ], "requestBody": { - "description": "The attributes and relationships of the player to update. Omitted fields are left unchanged.", + "description": "The attributes and relationships of the group to update. Omitted fields are left unchanged.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/playerPatchRequestDocument" + "$ref": "#/components/schemas/groupPatchRequestDocument" } ] } @@ -720,17 +1014,17 @@ }, "responses": { "200": { - "description": "The player was successfully updated, which resulted in additional changes. The updated player is returned.", + "description": "The group was successfully updated, which resulted in additional changes. The updated group is returned.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" + "$ref": "#/components/schemas/groupPrimaryResponseDocument" } } } }, "204": { - "description": "The player was successfully updated, which did not result in additional changes." + "description": "The group 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.", @@ -743,7 +1037,7 @@ } }, "404": { - "description": "The player or a related resource does not exist.", + "description": "The group or a related resource does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -776,15 +1070,15 @@ }, "delete": { "tags": [ - "players" + "groups" ], - "summary": "Deletes an existing player by its identifier.", - "operationId": "deletePlayer", + "summary": "Deletes an existing group by its identifier.", + "operationId": "deleteGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to delete.", + "description": "The identifier of the group to delete.", "required": true, "schema": { "type": "string" @@ -793,10 +1087,10 @@ ], "responses": { "204": { - "description": "The player was successfully deleted." + "description": "The group was successfully deleted." }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -808,18 +1102,18 @@ } } }, - "/players/{id}/games": { + "/groups/{id}/players": { "get": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves the related games of an individual player's games relationship.", - "operationId": "getPlayerGames", + "summary": "Retrieves the related players of an individual group's players relationship.", + "operationId": "getGroupPlayers", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related games to retrieve.", + "description": "The identifier of the group whose related players to retrieve.", "required": true, "schema": { "type": "string" @@ -837,15 +1131,44 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "Successfully returns the found games, or an empty array if none were found.", + "description": "Successfully returns the found players, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/gameCollectionResponseDocument" + "$ref": "#/components/schemas/playerCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" } } } @@ -861,7 +1184,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -874,16 +1197,16 @@ }, "head": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves the related games of an individual player's games relationship without returning them.", + "summary": "Retrieves the related players of an individual group's players 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": "headPlayerGames", + "operationId": "headGroupPlayers", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related games to retrieve.", + "description": "The identifier of the group whose related players to retrieve.", "required": true, "schema": { "type": "string" @@ -901,33 +1224,70 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." }, "404": { - "description": "The player does not exist." + "description": "The group does not exist." } } } }, - "/players/{id}/relationships/games": { + "/groups/{id}/relationships/players": { "get": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves the related game identities of an individual player's games relationship.", - "operationId": "getPlayerGamesRelationship", + "summary": "Retrieves the related player identities of an individual group's players relationship.", + "operationId": "getGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", + "description": "The identifier of the group whose related player identities to retrieve.", "required": true, "schema": { "type": "string" @@ -945,15 +1305,44 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "Successfully returns the found game identities, or an empty array if none were found.", + "description": "Successfully returns the found player identities, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/playerIdentifierCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" } } } @@ -969,7 +1358,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -982,16 +1371,16 @@ }, "head": { "tags": [ - "players" + "groups" ], - "summary": "Retrieves the related game identities of an individual player's games relationship without returning them.", + "summary": "Retrieves the related player identities of an individual group's players 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": "headPlayerGamesRelationship", + "operationId": "headGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", + "description": "The identifier of the group whose related player identities to retrieve.", "required": true, "schema": { "type": "string" @@ -1009,31 +1398,68 @@ }, "example": "" } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } ], "responses": { "200": { - "description": "The operation completed successfully." + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } }, "400": { "description": "The query string is invalid." }, "404": { - "description": "The player does not exist." + "description": "The group does not exist." } } }, "post": { "tags": [ - "players" + "groups" ], - "summary": "Adds existing games to the games relationship of an individual player.", - "operationId": "postPlayerGamesRelationship", + "summary": "Adds existing players to the players relationship of an individual group.", + "operationId": "postGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to add games to.", + "description": "The identifier of the group to add players to.", "required": true, "schema": { "type": "string" @@ -1041,13 +1467,13 @@ } ], "requestBody": { - "description": "The identities of the games to add to the games relationship.", + "description": "The identities of the players to add to the players relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerInRequest" } ] } @@ -1056,7 +1482,7 @@ }, "responses": { "204": { - "description": "The games were successfully added, which did not result in additional changes." + "description": "The players were successfully added, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -1069,7 +1495,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1092,15 +1518,15 @@ }, "patch": { "tags": [ - "players" + "groups" ], - "summary": "Assigns existing games to the games relationship of an individual player.", - "operationId": "patchPlayerGamesRelationship", + "summary": "Assigns existing players to the players relationship of an individual group.", + "operationId": "patchGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose games relationship to assign.", + "description": "The identifier of the group whose players relationship to assign.", "required": true, "schema": { "type": "string" @@ -1108,13 +1534,13 @@ } ], "requestBody": { - "description": "The identities of the games to assign to the games relationship, or an empty array to clear the relationship.", + "description": "The identities of the players to assign to the players relationship, or an empty array to clear the relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerInRequest" } ] } @@ -1123,7 +1549,7 @@ }, "responses": { "204": { - "description": "The games relationship was successfully updated, which did not result in additional changes." + "description": "The players relationship was successfully updated, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -1136,7 +1562,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1159,15 +1585,15 @@ }, "delete": { "tags": [ - "players" + "groups" ], - "summary": "Removes existing games from the games relationship of an individual player.", - "operationId": "deletePlayerGamesRelationship", + "summary": "Removes existing players from the players relationship of an individual group.", + "operationId": "deleteGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to remove games from.", + "description": "The identifier of the group to remove players from.", "required": true, "schema": { "type": "string" @@ -1175,13 +1601,13 @@ } ], "requestBody": { - "description": "The identities of the games to remove from the games relationship.", + "description": "The identities of the players to remove from the players relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerInRequest" } ] } @@ -1190,7 +1616,7 @@ }, "responses": { "204": { - "description": "The games were successfully removed, which did not result in additional changes." + "description": "The players were successfully removed, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -1203,7 +1629,7 @@ } }, "404": { - "description": "The player does not exist.", + "description": "The group does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1224,143 +1650,2065 @@ } } } - } - }, - "components": { - "schemas": { - "dataInResponse": { - "required": [ - "id", - "type" + }, + "/players": { + "get": { + "tags": [ + "players" ], - "type": "object", - "properties": { - "type": { - "minLength": 1, - "type": "string" + "summary": "Retrieves a collection of players.", + "operationId": "getPlayerCollection", + "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": "" + } }, - "id": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false, - "discriminator": { - "propertyName": "type", - "mapping": { - "games": "#/components/schemas/gameDataInResponse", - "players": "#/components/schemas/playerDataInResponse" + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } } - }, - "x-abstract": true - }, - "errorLinks": { - "type": "object", - "properties": { - "about": { - "type": "string", - "nullable": true + ], + "responses": { + "200": { + "description": "Successfully returns the found players, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerCollectionResponseDocument" + } + } + } }, - "type": { - "type": "string", - "nullable": true + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } } - }, - "additionalProperties": false + } }, - "errorObject": { - "type": "object", - "properties": { - "id": { - "type": "string", - "nullable": true + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves a collection of players 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": "headPlayerCollection", + "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": "" + } }, - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/errorLinks" + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } } - ], - "nullable": true - }, - "status": { - "type": "string" - }, - "code": { - "type": "string", - "nullable": true - }, - "title": { - "type": "string", - "nullable": true - }, - "detail": { - "type": "string", - "nullable": true + } }, - "source": { - "allOf": [ - { - "$ref": "#/components/schemas/errorSource" + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } } - ], - "nullable": true + } }, - "meta": { - "type": "object", - "additionalProperties": { }, - "nullable": true + "400": { + "description": "The query string is invalid." } - }, - "additionalProperties": false + } }, - "errorResponseDocument": { - "required": [ - "errors" + "post": { + "tags": [ + "players" ], - "type": "object", - "properties": { - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/errorObject" + "summary": "Creates a new player.", + "operationId": "postPlayer", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the player to create.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/playerPostRequestDocument" + } + ] + } + } + } + }, + "responses": { + "201": { + "description": "The player was successfully created, which resulted in additional changes. The newly created player is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created player can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The player was successfully created, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players/{id}": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves an individual player by its identifier.", + "operationId": "getPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found player.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves an individual player 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": "headPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + }, + "patch": { + "tags": [ + "players" + ], + "summary": "Updates an existing player.", + "operationId": "patchPlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to update.", + "required": true, + "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": "" + } + } + ], + "requestBody": { + "description": "The attributes and relationships of the player to update. Omitted fields are left unchanged.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/playerPatchRequestDocument" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "The player was successfully updated, which resulted in additional changes. The updated player is returned.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, + "204": { + "description": "The player was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The query string is invalid or the request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player or a related resource does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type or identifier in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "422": { + "description": "Validation of the request body failed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "players" + ], + "summary": "Deletes an existing player by its identifier.", + "operationId": "deletePlayer", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to delete.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The player was successfully deleted." + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players/{id}/games": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related games of an individual player's games relationship.", + "operationId": "getPlayerGames", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related games to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found games, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gameCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related games of an individual player's games 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": "headPlayerGames", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related games to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + } + }, + "/players/{id}/relationships/games": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related game identities of an individual player's games relationship.", + "operationId": "getPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related game identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found game identities, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related game identities of an individual player's games 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": "headPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related game identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + }, + "post": { + "tags": [ + "players" + ], + "summary": "Adds existing games to the games relationship of an individual player.", + "operationId": "postPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to add games to.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to add to the games relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "players" + ], + "summary": "Assigns existing games to the games relationship of an individual player.", + "operationId": "patchPlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose games relationship to assign.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to assign to the games relationship, or an empty array to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "players" + ], + "summary": "Removes existing games from the games relationship of an individual player.", + "operationId": "deletePlayerGamesRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to remove games from.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the games to remove from the games relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGameInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The games were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + }, + "/players/{id}/groups": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related groups of an individual player's groups relationship.", + "operationId": "getPlayerGroups", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related groups to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found groups, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/groupCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related groups of an individual player's groups 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": "headPlayerGroups", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related groups to retrieve.", + "required": true, + "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": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + } + }, + "/players/{id}/relationships/groups": { + "get": { + "tags": [ + "players" + ], + "summary": "Retrieves the related group identities of an individual player's groups relationship.", + "operationId": "getPlayerGroupsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related group identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully returns the found group identities, or an empty array if none were found.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/groupIdentifierCollectionResponseDocument" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "head": { + "tags": [ + "players" + ], + "summary": "Retrieves the related group identities of an individual player's groups 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": "headPlayerGroupsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose related group identities to retrieve.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" + } + }, + { + "name": "If-None-Match", + "in": "header", + "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The operation completed successfully.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + }, + "Content-Length": { + "description": "Size of the HTTP response body, in bytes.", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + } + }, + "304": { + "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", + "headers": { + "ETag": { + "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "The query string is invalid." + }, + "404": { + "description": "The player does not exist." + } + } + }, + "post": { + "tags": [ + "players" + ], + "summary": "Adds existing groups to the groups relationship of an individual player.", + "operationId": "postPlayerGroupsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to add groups to.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the groups to add to the groups relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The groups were successfully added, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "patch": { + "tags": [ + "players" + ], + "summary": "Assigns existing groups to the groups relationship of an individual player.", + "operationId": "patchPlayerGroupsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player whose groups relationship to assign.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the groups to assign to the groups relationship, or an empty array to clear the relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The groups relationship was successfully updated, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + }, + "delete": { + "tags": [ + "players" + ], + "summary": "Removes existing groups from the groups relationship of an individual player.", + "operationId": "deletePlayerGroupsRelationship", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The identifier of the player to remove groups from.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The identities of the groups to remove from the groups relationship.", + "content": { + "application/vnd.api+json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInRequest" + } + ] + } + } + } + }, + "responses": { + "204": { + "description": "The groups were successfully removed, which did not result in additional changes." + }, + "400": { + "description": "The request body is missing or malformed.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "404": { + "description": "The player does not exist.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + }, + "409": { + "description": "A resource type in the request body is incompatible.", + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/errorResponseDocument" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "dataInResponse": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "minLength": 1, + "type": "string" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "type", + "mapping": { + "games": "#/components/schemas/gameDataInResponse", + "groups": "#/components/schemas/groupDataInResponse", + "players": "#/components/schemas/playerDataInResponse" + } + }, + "x-abstract": true + }, + "errorLinks": { + "type": "object", + "properties": { + "about": { + "type": "string", + "nullable": true + }, + "type": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "errorObject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/errorLinks" + } + ], + "nullable": true + }, + "status": { + "type": "string" + }, + "code": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/errorSource" + } + ], + "nullable": true + }, + "meta": { + "type": "object", + "additionalProperties": { }, + "nullable": true + } + }, + "additionalProperties": false + }, + "errorResponseDocument": { + "required": [ + "errors" + ], + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/errorObject" + } + } + }, + "additionalProperties": false + }, + "errorSource": { + "type": "object", + "properties": { + "pointer": { + "type": "string", + "nullable": true + }, + "parameter": { + "type": "string", + "nullable": true + }, + "header": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "gameAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameAttributesInPostRequest": { + "required": [ + "name" + ], + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameAttributesInResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "price": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "gameCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameDataInResponse" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "gameDataInPatchRequest": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gameDataInPostRequest": { + "required": [ + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gameDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "required": [ + "links" + ], + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/gameAttributesInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceData" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + } + ], + "additionalProperties": false + }, + "gameIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/gameResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "gameIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/gameIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "gamePatchRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInPatchRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gamePostRequestDocument": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInPostRequest" + } + ] + } + }, + "additionalProperties": false + }, + "gamePrimaryResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceDocument" + } + ] + }, + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/gameDataInResponse" + } + ] + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true } } }, "additionalProperties": false }, - "errorSource": { - "type": "object", - "properties": { - "pointer": { - "type": "string", - "nullable": true - }, - "parameter": { - "type": "string", - "nullable": true - }, - "header": { - "type": "string", - "nullable": true - } - }, + "gameResourceType": { + "enum": [ + "games" + ], + "type": "string", "additionalProperties": false }, - "gameAttributesInPatchRequest": { + "groupAttributesInPatchRequest": { "type": "object", "properties": { "name": { "type": "string" - }, - "price": { - "type": "number", - "format": "double" } }, "additionalProperties": false }, - "gameAttributesInPostRequest": { + "groupAttributesInPostRequest": { "required": [ "name" ], @@ -1368,28 +3716,20 @@ "properties": { "name": { "type": "string" - }, - "price": { - "type": "number", - "format": "double" } }, "additionalProperties": false }, - "gameAttributesInResponse": { + "groupAttributesInResponse": { "type": "object", "properties": { "name": { "type": "string" - }, - "price": { - "type": "number", - "format": "double" } }, "additionalProperties": false }, - "gameCollectionResponseDocument": { + "groupCollectionResponseDocument": { "required": [ "data", "links" @@ -1406,7 +3746,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/gameDataInResponse" + "$ref": "#/components/schemas/groupDataInResponse" } }, "included": { @@ -1425,7 +3765,7 @@ }, "additionalProperties": false }, - "gameDataInPatchRequest": { + "groupDataInPatchRequest": { "required": [ "id", "type" @@ -1433,7 +3773,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/gameResourceType" + "$ref": "#/components/schemas/groupResourceType" }, "id": { "minLength": 1, @@ -1442,37 +3782,47 @@ "attributes": { "allOf": [ { - "$ref": "#/components/schemas/gameAttributesInPatchRequest" + "$ref": "#/components/schemas/groupAttributesInPatchRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/groupRelationshipsInPatchRequest" } ] } }, "additionalProperties": false }, - "gameDataInPostRequest": { + "groupDataInPostRequest": { "required": [ "type" ], "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/gameResourceType" - }, - "id": { - "minLength": 1, - "type": "string" + "$ref": "#/components/schemas/groupResourceType" }, "attributes": { "allOf": [ { - "$ref": "#/components/schemas/gameAttributesInPostRequest" + "$ref": "#/components/schemas/groupAttributesInPostRequest" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/groupRelationshipsInPostRequest" } ] } }, "additionalProperties": false }, - "gameDataInResponse": { + "groupDataInResponse": { "allOf": [ { "$ref": "#/components/schemas/dataInResponse" @@ -1486,7 +3836,14 @@ "attributes": { "allOf": [ { - "$ref": "#/components/schemas/gameAttributesInResponse" + "$ref": "#/components/schemas/groupAttributesInResponse" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/groupRelationshipsInResponse" } ] }, @@ -1510,7 +3867,7 @@ ], "additionalProperties": false }, - "gameIdentifier": { + "groupIdentifier": { "required": [ "id", "type" @@ -1518,7 +3875,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/gameResourceType" + "$ref": "#/components/schemas/groupResourceType" }, "id": { "minLength": 1, @@ -1527,7 +3884,7 @@ }, "additionalProperties": false }, - "gameIdentifierCollectionResponseDocument": { + "groupIdentifierCollectionResponseDocument": { "required": [ "data", "links" @@ -1544,7 +3901,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/gameIdentifier" + "$ref": "#/components/schemas/groupIdentifier" } }, "meta": { @@ -1557,7 +3914,7 @@ }, "additionalProperties": false }, - "gamePatchRequestDocument": { + "groupPatchRequestDocument": { "required": [ "data" ], @@ -1566,14 +3923,14 @@ "data": { "allOf": [ { - "$ref": "#/components/schemas/gameDataInPatchRequest" + "$ref": "#/components/schemas/groupDataInPatchRequest" } ] } }, "additionalProperties": false }, - "gamePostRequestDocument": { + "groupPostRequestDocument": { "required": [ "data" ], @@ -1582,14 +3939,14 @@ "data": { "allOf": [ { - "$ref": "#/components/schemas/gameDataInPostRequest" + "$ref": "#/components/schemas/groupDataInPostRequest" } ] } }, "additionalProperties": false }, - "gamePrimaryResponseDocument": { + "groupPrimaryResponseDocument": { "required": [ "data", "links" @@ -1606,7 +3963,7 @@ "data": { "allOf": [ { - "$ref": "#/components/schemas/gameDataInResponse" + "$ref": "#/components/schemas/groupDataInResponse" } ] }, @@ -1626,9 +3983,48 @@ }, "additionalProperties": false }, - "gameResourceType": { + "groupRelationshipsInPatchRequest": { + "type": "object", + "properties": { + "players": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyPlayerInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "groupRelationshipsInPostRequest": { + "type": "object", + "properties": { + "players": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyPlayerInRequest" + } + ] + } + }, + "additionalProperties": false + }, + "groupRelationshipsInResponse": { + "type": "object", + "properties": { + "players": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyPlayerInResponse" + } + ] + } + }, + "additionalProperties": false + }, + "groupResourceType": { "enum": [ - "games" + "groups" ], "type": "string", "additionalProperties": false @@ -1914,6 +4310,53 @@ ], "additionalProperties": false }, + "playerIdentifier": { + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/playerResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + } + }, + "additionalProperties": false + }, + "playerIdentifierCollectionResponseDocument": { + "required": [ + "data", + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/playerIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, "playerPatchRequestDocument": { "required": [ "data" @@ -1992,6 +4435,13 @@ "$ref": "#/components/schemas/toManyGameInRequest" } ] + }, + "groups": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInRequest" + } + ] } }, "additionalProperties": false @@ -2005,6 +4455,13 @@ "$ref": "#/components/schemas/toManyGameInRequest" } ] + }, + "groups": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInRequest" + } + ] } }, "additionalProperties": false @@ -2018,6 +4475,13 @@ "$ref": "#/components/schemas/toManyGameInResponse" } ] + }, + "groups": { + "allOf": [ + { + "$ref": "#/components/schemas/toManyGroupInResponse" + } + ] } }, "additionalProperties": false @@ -2072,6 +4536,94 @@ } }, "additionalProperties": false + }, + "toManyGroupInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/groupIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyGroupInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationship" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/groupIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "toManyPlayerInRequest": { + "required": [ + "data" + ], + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/playerIdentifier" + } + } + }, + "additionalProperties": false + }, + "toManyPlayerInResponse": { + "required": [ + "links" + ], + "type": "object", + "properties": { + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInRelationship" + } + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/playerIdentifier" + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } + } + }, + "additionalProperties": false } } } diff --git a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj index c6381e9737..17636ff293 100644 --- a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj +++ b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj @@ -28,7 +28,7 @@ ClientGeneratedIdClient ClientGeneratedIdClient.cs NSwagCSharp - /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client.Exceptions /GenerateNullableReferenceTypes:true + /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client /GenerateNullableReferenceTypes:true OpenApiEndToEndTests.Headers.GeneratedCode diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs index a7faee6027..6ea2f56513 100644 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs @@ -9,4 +9,5 @@ public sealed class ClientGeneratedIdDbContext(DbContextOptions Players => Set(); public DbSet Games => Set(); + public DbSet Groups => Set(); } diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs index 3f4cf18b9a..5f14dc74a7 100644 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs @@ -19,6 +19,11 @@ public sealed class ClientGeneratedIdFakers : FakerContainer .RuleFor(player => player.Name, faker => faker.Commerce.ProductName()) .RuleFor(player => player.Price, faker => decimal.Parse(faker.Commerce.Price()))); + private readonly Lazy> _lazyGroupFaker = new(() => new Faker() + .UseSeed(GetFakerSeed()) + .RuleFor(group => group.Name, faker => faker.Person.Company.Name)); + public Faker Player => _lazyPlayerFaker.Value; public Faker Game => _lazyGameFaker.Value; + public Faker Group => _lazyGroupFaker.Value; } diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs index 7c6e03f822..6b15fa0167 100644 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs +++ b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs @@ -14,6 +14,7 @@ public ClientGeneratedIdTests(OpenApiTestContext(); testContext.UseController(); + testContext.UseController(); testContext.SwaggerDocumentOutputDirectory = "test/OpenApiEndToEndTests/ClientGeneratedId"; } @@ -25,7 +26,7 @@ public async Task Post_data_should_have_required_id() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.Should().ContainPath($"components.schemas.playerDataInPostRequest").With(resourceDataInPostRequestElement => + document.Should().ContainPath("components.schemas.playerDataInPostRequest").With(resourceDataInPostRequestElement => { resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => { @@ -43,7 +44,7 @@ public async Task Post_data_should_have_non_required_id() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.Should().ContainPath($"components.schemas.gameDataInPostRequest").With(resourceDataInPostRequestElement => + document.Should().ContainPath("components.schemas.gameDataInPostRequest").With(resourceDataInPostRequestElement => { resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => { @@ -53,4 +54,22 @@ public async Task Post_data_should_have_non_required_id() resourceDataInPostRequestElement.Should().ContainPath("properties.id"); }); } + + [Fact] + public async Task Post_data_should_not_have_id() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.groupDataInPostRequest").With(resourceDataInPostRequestElement => + { + resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + { + requiredElement.Should().NotContainArrayElement("id"); + }); + + resourceDataInPostRequestElement.Should().NotContainPath("properties.id"); + }); + } } diff --git a/test/OpenApiTests/ClientGeneratedId/Game.cs b/test/OpenApiTests/ClientGeneratedId/Game.cs index 18e9574520..ca8c4fc36b 100644 --- a/test/OpenApiTests/ClientGeneratedId/Game.cs +++ b/test/OpenApiTests/ClientGeneratedId/Game.cs @@ -7,7 +7,7 @@ namespace OpenApiTests.ClientGeneratedId; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Allowed)] -public sealed class Game : Identifiable +public sealed class Game : Identifiable { [Attr] public string Name { get; set; } = null!; diff --git a/test/OpenApiTests/ClientGeneratedId/Group.cs b/test/OpenApiTests/ClientGeneratedId/Group.cs new file mode 100644 index 0000000000..f1b27dca3f --- /dev/null +++ b/test/OpenApiTests/ClientGeneratedId/Group.cs @@ -0,0 +1,17 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ClientGeneratedId; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Forbidden)] +public sealed class Group : Identifiable +{ + [Attr] + public string Name { get; set; } = null!; + + [HasMany] + public List Players { get; set; } = []; +} diff --git a/test/OpenApiTests/ClientGeneratedId/Player.cs b/test/OpenApiTests/ClientGeneratedId/Player.cs index 5aa9d39132..610298a80d 100644 --- a/test/OpenApiTests/ClientGeneratedId/Player.cs +++ b/test/OpenApiTests/ClientGeneratedId/Player.cs @@ -7,11 +7,14 @@ namespace OpenApiTests.ClientGeneratedId; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Required)] -public sealed class Player : Identifiable +public sealed class Player : Identifiable { [Attr] public string Name { get; set; } = null!; [HasMany] public List Games { get; set; } = []; + + [HasMany] + public List Groups { get; set; } = []; } From 05b4ba5d65d178bdc3742ee6f71c39f2b4e9c7c4 Mon Sep 17 00:00:00 2001 From: verdie-g Date: Fri, 23 Feb 2024 22:13:02 -0500 Subject: [PATCH 3/6] Address PR comments --- .../PostTests.cs | 67 +- .../swagger.g.json | 944 +++++++++--------- .../OpenApiEndToEndTests.csproj | 8 +- .../ClientGeneratedIdDbContext.cs | 13 - test/OpenApiTests/ClientGeneratedId/Player.cs | 20 - .../ClientGeneratedIdDbContext.cs | 13 + .../ClientGeneratedIdFakers.cs | 16 +- .../ClientGeneratedIdTests.cs | 31 +- .../Game.cs | 8 +- .../ClientIdGenerationModes/Player.cs | 20 + .../PlayerGroup.cs} | 6 +- 11 files changed, 574 insertions(+), 572 deletions(-) rename test/OpenApiEndToEndTests/{ClientGeneratedId => ClientIdGenerationModes}/PostTests.cs (64%) rename test/OpenApiEndToEndTests/{ClientGeneratedId => ClientIdGenerationModes}/swagger.g.json (91%) delete mode 100644 test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs delete mode 100644 test/OpenApiTests/ClientGeneratedId/Player.cs create mode 100644 test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs rename test/OpenApiTests/{ClientGeneratedId => ClientIdGenerationModes}/ClientGeneratedIdFakers.cs (50%) rename test/OpenApiTests/{ClientGeneratedId => ClientIdGenerationModes}/ClientGeneratedIdTests.cs (50%) rename test/OpenApiTests/{ClientGeneratedId => ClientIdGenerationModes}/Game.cs (50%) create mode 100644 test/OpenApiTests/ClientIdGenerationModes/Player.cs rename test/OpenApiTests/{ClientGeneratedId/Group.cs => ClientIdGenerationModes/PlayerGroup.cs} (58%) diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs b/test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs similarity index 64% rename from test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs rename to test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs index aad7e9da25..622451ac78 100644 --- a/test/OpenApiEndToEndTests/ClientGeneratedId/PostTests.cs +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs @@ -2,36 +2,36 @@ using FluentAssertions.Specialized; using JsonApiDotNetCore.OpenApi.Client; using Newtonsoft.Json; -using OpenApiEndToEndTests.ClientGeneratedId.GeneratedCode; +using OpenApiEndToEndTests.ClientIdGenerationModes.GeneratedCode; using OpenApiTests; -using OpenApiTests.ClientGeneratedId; +using OpenApiTests.ClientIdGenerationModes; using TestBuildingBlocks; using Xunit; -namespace OpenApiEndToEndTests.ClientGeneratedId; +namespace OpenApiEndToEndTests.ClientIdGenerationModes; -public sealed class PostTests : IClassFixture, ClientGeneratedIdDbContext>> +public sealed class PostTests : IClassFixture, ClientIdGenerationModesDbContext>> { - private readonly IntegrationTestContext, ClientGeneratedIdDbContext> _testContext; - private readonly ClientGeneratedIdFakers _fakers = new(); + private readonly IntegrationTestContext, ClientIdGenerationModesDbContext> _testContext; + private readonly ClientIdGenerationModesFakers _fakers = new(); - public PostTests(IntegrationTestContext, ClientGeneratedIdDbContext> testContext) + public PostTests(IntegrationTestContext, ClientIdGenerationModesDbContext> testContext) { _testContext = testContext; testContext.UseController(); testContext.UseController(); - testContext.UseController(); + testContext.UseController(); } [Fact] - public async Task Omit_required_id() + public async Task Cannot_create_resource_without_ID_when_mode_is_required() { // Arrange Player player = _fakers.Player.Generate(); using HttpClient httpClient = _testContext.Factory.CreateClient(); - ClientGeneratedIdClient apiClient = new(httpClient); + ClientIdGenerationModesClient apiClient = new(httpClient); // Act Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument @@ -41,7 +41,7 @@ public async Task Omit_required_id() Id = null!, Attributes = new PlayerAttributesInPostRequest { - Name = player.Name + UserName = player.UserName } } })); @@ -52,14 +52,14 @@ public async Task Omit_required_id() } [Fact] - public async Task Pass_required_id() + public async Task Can_create_resource_with_ID_when_mode_is_required() { // Arrange Player player = _fakers.Player.Generate(); player.Id = Guid.NewGuid(); using HttpClient httpClient = _testContext.Factory.CreateClient(); - ClientGeneratedIdClient apiClient = new(httpClient); + ClientIdGenerationModesClient apiClient = new(httpClient); // Act Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument @@ -69,7 +69,7 @@ public async Task Pass_required_id() Id = player.StringId!, Attributes = new PlayerAttributesInPostRequest { - Name = player.Name + UserName = player.UserName } } })); @@ -80,13 +80,13 @@ public async Task Pass_required_id() } [Fact] - public async Task Omit_allowed_id() + public async Task Can_create_resource_without_ID_when_mode_is_allowed() { // Arrange Game game = _fakers.Game.Generate(); using HttpClient httpClient = _testContext.Factory.CreateClient(); - ClientGeneratedIdClient apiClient = new(httpClient); + ClientIdGenerationModesClient apiClient = new(httpClient); // Act Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument @@ -96,8 +96,8 @@ public async Task Omit_allowed_id() Id = null!, Attributes = new GameAttributesInPostRequest { - Name = game.Name, - Price = (double)game.Price + Title = game.Title, + PurchasePrice = (double)game.PurchasePrice } } })); @@ -108,14 +108,14 @@ public async Task Omit_allowed_id() } [Fact] - public async Task Pass_allowed_id() + public async Task Can_create_resource_with_ID_when_mode_is_allowed() { // Arrange Game game = _fakers.Game.Generate(); game.Id = Guid.NewGuid(); using HttpClient httpClient = _testContext.Factory.CreateClient(); - ClientGeneratedIdClient apiClient = new(httpClient); + ClientIdGenerationModesClient apiClient = new(httpClient); // Act Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument @@ -125,8 +125,8 @@ public async Task Pass_allowed_id() Id = game.StringId!, Attributes = new GameAttributesInPostRequest { - Name = game.Name, - Price = (double)game.Price + Title = game.Title, + PurchasePrice = (double)game.PurchasePrice } } })); @@ -137,28 +137,29 @@ public async Task Pass_allowed_id() } [Fact] - public async Task Omit_forbidden_id() + public async Task Can_create_resource_without_ID_when_mode_is_forbidden() { // Arrange - Group group = _fakers.Group.Generate(); + PlayerGroup playerGroup = _fakers.Group.Generate(); using HttpClient httpClient = _testContext.Factory.CreateClient(); - ClientGeneratedIdClient apiClient = new(httpClient); + ClientIdGenerationModesClient apiClient = new(httpClient); // Act - Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGroupAsync(null, new GroupPostRequestDocument - { - Data = new GroupDataInPostRequest + Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerGroupAsync(null, + new PlayerGroupPostRequestDocument { - Attributes = new GroupAttributesInPostRequest + Data = new PlayerGroupDataInPostRequest { - Name = group.Name + Attributes = new PlayerGroupAttributesInPostRequest + { + Name = playerGroup.Name + } } - } - })); + })); // Assert - GroupPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; + PlayerGroupPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; doc?.Data.Id.Should().NotBeNullOrEmpty(); } } diff --git a/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json similarity index 91% rename from test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json rename to test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json index e1f5bbcf8c..1258d1b713 100644 --- a/test/OpenApiEndToEndTests/ClientGeneratedId/swagger.g.json +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json @@ -551,13 +551,13 @@ } } }, - "/groups": { + "/playerGroups": { "get": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves a collection of groups.", - "operationId": "getGroupCollection", + "summary": "Retrieves a collection of playerGroups.", + "operationId": "getPlayerGroupCollection", "parameters": [ { "name": "query", @@ -583,7 +583,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found groups, or an empty array if none were found.", + "description": "Successfully returns the found playerGroups, or an empty array if none were found.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -596,7 +596,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupCollectionResponseDocument" + "$ref": "#/components/schemas/playerGroupCollectionResponseDocument" } } } @@ -627,11 +627,11 @@ }, "head": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves a collection of groups without returning them.", + "summary": "Retrieves a collection of playerGroups 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": "headGroupCollection", + "operationId": "headPlayerGroupCollection", "parameters": [ { "name": "query", @@ -695,10 +695,10 @@ }, "post": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Creates a new group.", - "operationId": "postGroup", + "summary": "Creates a new playerGroup.", + "operationId": "postPlayerGroup", "parameters": [ { "name": "query", @@ -715,13 +715,13 @@ } ], "requestBody": { - "description": "The attributes and relationships of the group to create.", + "description": "The attributes and relationships of the playerGroup to create.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/groupPostRequestDocument" + "$ref": "#/components/schemas/playerGroupPostRequestDocument" } ] } @@ -730,10 +730,10 @@ }, "responses": { "201": { - "description": "The group was successfully created, which resulted in additional changes. The newly created group is returned.", + "description": "The playerGroup was successfully created, which resulted in additional changes. The newly created playerGroup is returned.", "headers": { "Location": { - "description": "The URL at which the newly created group can be retrieved.", + "description": "The URL at which the newly created playerGroup can be retrieved.", "required": true, "schema": { "type": "string", @@ -744,13 +744,13 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupPrimaryResponseDocument" + "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" } } } }, "204": { - "description": "The group was successfully created, which did not result in additional changes." + "description": "The playerGroup 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.", @@ -795,18 +795,18 @@ } } }, - "/groups/{id}": { + "/playerGroups/{id}": { "get": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves an individual group by its identifier.", - "operationId": "getGroup", + "summary": "Retrieves an individual playerGroup by its identifier.", + "operationId": "getPlayerGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to retrieve.", + "description": "The identifier of the playerGroup to retrieve.", "required": true, "schema": { "type": "string" @@ -836,7 +836,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found group.", + "description": "Successfully returns the found playerGroup.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -849,7 +849,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupPrimaryResponseDocument" + "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" } } } @@ -877,7 +877,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -890,16 +890,16 @@ }, "head": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves an individual group by its identifier without returning it.", + "summary": "Retrieves an individual playerGroup 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": "headGroup", + "operationId": "headPlayerGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to retrieve.", + "description": "The identifier of the playerGroup to retrieve.", "required": true, "schema": { "type": "string" @@ -964,21 +964,21 @@ "description": "The query string is invalid." }, "404": { - "description": "The group does not exist." + "description": "The playerGroup does not exist." } } }, "patch": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Updates an existing group.", - "operationId": "patchGroup", + "summary": "Updates an existing playerGroup.", + "operationId": "patchPlayerGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to update.", + "description": "The identifier of the playerGroup to update.", "required": true, "schema": { "type": "string" @@ -999,13 +999,13 @@ } ], "requestBody": { - "description": "The attributes and relationships of the group to update. Omitted fields are left unchanged.", + "description": "The attributes and relationships of the playerGroup to update. Omitted fields are left unchanged.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/groupPatchRequestDocument" + "$ref": "#/components/schemas/playerGroupPatchRequestDocument" } ] } @@ -1014,17 +1014,17 @@ }, "responses": { "200": { - "description": "The group was successfully updated, which resulted in additional changes. The updated group is returned.", + "description": "The playerGroup was successfully updated, which resulted in additional changes. The updated playerGroup is returned.", "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupPrimaryResponseDocument" + "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" } } } }, "204": { - "description": "The group was successfully updated, which did not result in additional changes." + "description": "The playerGroup 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.", @@ -1037,7 +1037,7 @@ } }, "404": { - "description": "The group or a related resource does not exist.", + "description": "The playerGroup or a related resource does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1070,15 +1070,15 @@ }, "delete": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Deletes an existing group by its identifier.", - "operationId": "deleteGroup", + "summary": "Deletes an existing playerGroup by its identifier.", + "operationId": "deletePlayerGroup", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to delete.", + "description": "The identifier of the playerGroup to delete.", "required": true, "schema": { "type": "string" @@ -1087,10 +1087,10 @@ ], "responses": { "204": { - "description": "The group was successfully deleted." + "description": "The playerGroup was successfully deleted." }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1102,18 +1102,18 @@ } } }, - "/groups/{id}/players": { + "/playerGroups/{id}/players": { "get": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves the related players of an individual group's players relationship.", - "operationId": "getGroupPlayers", + "summary": "Retrieves the related players of an individual playerGroup's players relationship.", + "operationId": "getPlayerGroupPlayers", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group whose related players to retrieve.", + "description": "The identifier of the playerGroup whose related players to retrieve.", "required": true, "schema": { "type": "string" @@ -1184,7 +1184,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1197,16 +1197,16 @@ }, "head": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves the related players of an individual group's players relationship without returning them.", + "summary": "Retrieves the related players of an individual playerGroup's players 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": "headGroupPlayers", + "operationId": "headPlayerGroupPlayers", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group whose related players to retrieve.", + "description": "The identifier of the playerGroup whose related players to retrieve.", "required": true, "schema": { "type": "string" @@ -1271,23 +1271,23 @@ "description": "The query string is invalid." }, "404": { - "description": "The group does not exist." + "description": "The playerGroup does not exist." } } } }, - "/groups/{id}/relationships/players": { + "/playerGroups/{id}/relationships/players": { "get": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves the related player identities of an individual group's players relationship.", - "operationId": "getGroupPlayersRelationship", + "summary": "Retrieves the related player identities of an individual playerGroup's players relationship.", + "operationId": "getPlayerGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group whose related player identities to retrieve.", + "description": "The identifier of the playerGroup whose related player identities to retrieve.", "required": true, "schema": { "type": "string" @@ -1358,7 +1358,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1371,16 +1371,16 @@ }, "head": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Retrieves the related player identities of an individual group's players relationship without returning them.", + "summary": "Retrieves the related player identities of an individual playerGroup's players 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": "headGroupPlayersRelationship", + "operationId": "headPlayerGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group whose related player identities to retrieve.", + "description": "The identifier of the playerGroup whose related player identities to retrieve.", "required": true, "schema": { "type": "string" @@ -1445,21 +1445,21 @@ "description": "The query string is invalid." }, "404": { - "description": "The group does not exist." + "description": "The playerGroup does not exist." } } }, "post": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Adds existing players to the players relationship of an individual group.", - "operationId": "postGroupPlayersRelationship", + "summary": "Adds existing players to the players relationship of an individual playerGroup.", + "operationId": "postPlayerGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to add players to.", + "description": "The identifier of the playerGroup to add players to.", "required": true, "schema": { "type": "string" @@ -1495,7 +1495,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1518,15 +1518,15 @@ }, "patch": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Assigns existing players to the players relationship of an individual group.", - "operationId": "patchGroupPlayersRelationship", + "summary": "Assigns existing players to the players relationship of an individual playerGroup.", + "operationId": "patchPlayerGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group whose players relationship to assign.", + "description": "The identifier of the playerGroup whose players relationship to assign.", "required": true, "schema": { "type": "string" @@ -1562,7 +1562,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -1585,15 +1585,15 @@ }, "delete": { "tags": [ - "groups" + "playerGroups" ], - "summary": "Removes existing players from the players relationship of an individual group.", - "operationId": "deleteGroupPlayersRelationship", + "summary": "Removes existing players from the players relationship of an individual playerGroup.", + "operationId": "deletePlayerGroupPlayersRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the group to remove players from.", + "description": "The identifier of the playerGroup to remove players from.", "required": true, "schema": { "type": "string" @@ -1629,7 +1629,7 @@ } }, "404": { - "description": "The group does not exist.", + "description": "The playerGroup does not exist.", "content": { "application/vnd.api+json": { "schema": { @@ -2192,18 +2192,18 @@ } } }, - "/players/{id}/games": { + "/players/{id}/groupMemberships": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related games of an individual player's games relationship.", - "operationId": "getPlayerGames", + "summary": "Retrieves the related playerGroups of an individual player's groupMemberships relationship.", + "operationId": "getPlayerGroupMemberships", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related games to retrieve.", + "description": "The identifier of the player whose related playerGroups to retrieve.", "required": true, "schema": { "type": "string" @@ -2233,7 +2233,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found games, or an empty array if none were found.", + "description": "Successfully returns the found playerGroups, or an empty array if none were found.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -2246,7 +2246,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/gameCollectionResponseDocument" + "$ref": "#/components/schemas/playerGroupCollectionResponseDocument" } } } @@ -2289,14 +2289,14 @@ "tags": [ "players" ], - "summary": "Retrieves the related games of an individual player's games relationship without returning them.", + "summary": "Retrieves the related playerGroups of an individual player's groupMemberships 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": "headPlayerGames", + "operationId": "headPlayerGroupMemberships", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related games to retrieve.", + "description": "The identifier of the player whose related playerGroups to retrieve.", "required": true, "schema": { "type": "string" @@ -2366,18 +2366,18 @@ } } }, - "/players/{id}/relationships/games": { + "/players/{id}/relationships/groupMemberships": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related game identities of an individual player's games relationship.", - "operationId": "getPlayerGamesRelationship", + "summary": "Retrieves the related playerGroup identities of an individual player's groupMemberships relationship.", + "operationId": "getPlayerGroupMembershipsRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", + "description": "The identifier of the player whose related playerGroup identities to retrieve.", "required": true, "schema": { "type": "string" @@ -2407,7 +2407,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found game identities, or an empty array if none were found.", + "description": "Successfully returns the found playerGroup identities, or an empty array if none were found.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -2420,7 +2420,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/playerGroupIdentifierCollectionResponseDocument" } } } @@ -2463,14 +2463,14 @@ "tags": [ "players" ], - "summary": "Retrieves the related game identities of an individual player's games relationship without returning them.", + "summary": "Retrieves the related playerGroup identities of an individual player's groupMemberships 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": "headPlayerGamesRelationship", + "operationId": "headPlayerGroupMembershipsRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", + "description": "The identifier of the player whose related playerGroup identities to retrieve.", "required": true, "schema": { "type": "string" @@ -2543,13 +2543,13 @@ "tags": [ "players" ], - "summary": "Adds existing games to the games relationship of an individual player.", - "operationId": "postPlayerGamesRelationship", + "summary": "Adds existing playerGroups to the groupMemberships relationship of an individual player.", + "operationId": "postPlayerGroupMembershipsRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to add games to.", + "description": "The identifier of the player to add playerGroups to.", "required": true, "schema": { "type": "string" @@ -2557,13 +2557,13 @@ } ], "requestBody": { - "description": "The identities of the games to add to the games relationship.", + "description": "The identities of the playerGroups to add to the groupMemberships relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerGroupInRequest" } ] } @@ -2572,7 +2572,7 @@ }, "responses": { "204": { - "description": "The games were successfully added, which did not result in additional changes." + "description": "The playerGroups were successfully added, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -2610,13 +2610,13 @@ "tags": [ "players" ], - "summary": "Assigns existing games to the games relationship of an individual player.", - "operationId": "patchPlayerGamesRelationship", + "summary": "Assigns existing playerGroups to the groupMemberships relationship of an individual player.", + "operationId": "patchPlayerGroupMembershipsRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose games relationship to assign.", + "description": "The identifier of the player whose groupMemberships relationship to assign.", "required": true, "schema": { "type": "string" @@ -2624,13 +2624,13 @@ } ], "requestBody": { - "description": "The identities of the games to assign to the games relationship, or an empty array to clear the relationship.", + "description": "The identities of the playerGroups to assign to the groupMemberships relationship, or an empty array to clear the relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerGroupInRequest" } ] } @@ -2639,7 +2639,7 @@ }, "responses": { "204": { - "description": "The games relationship was successfully updated, which did not result in additional changes." + "description": "The groupMemberships relationship was successfully updated, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -2677,13 +2677,13 @@ "tags": [ "players" ], - "summary": "Removes existing games from the games relationship of an individual player.", - "operationId": "deletePlayerGamesRelationship", + "summary": "Removes existing playerGroups from the groupMemberships relationship of an individual player.", + "operationId": "deletePlayerGroupMembershipsRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to remove games from.", + "description": "The identifier of the player to remove playerGroups from.", "required": true, "schema": { "type": "string" @@ -2691,13 +2691,13 @@ } ], "requestBody": { - "description": "The identities of the games to remove from the games relationship.", + "description": "The identities of the playerGroups to remove from the groupMemberships relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/toManyPlayerGroupInRequest" } ] } @@ -2706,7 +2706,7 @@ }, "responses": { "204": { - "description": "The games were successfully removed, which did not result in additional changes." + "description": "The playerGroups were successfully removed, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -2741,18 +2741,18 @@ } } }, - "/players/{id}/groups": { + "/players/{id}/ownedGames": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related groups of an individual player's groups relationship.", - "operationId": "getPlayerGroups", + "summary": "Retrieves the related games of an individual player's ownedGames relationship.", + "operationId": "getPlayerOwnedGames", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related groups to retrieve.", + "description": "The identifier of the player whose related games to retrieve.", "required": true, "schema": { "type": "string" @@ -2782,7 +2782,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found groups, or an empty array if none were found.", + "description": "Successfully returns the found games, or an empty array if none were found.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -2795,7 +2795,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupCollectionResponseDocument" + "$ref": "#/components/schemas/gameCollectionResponseDocument" } } } @@ -2838,14 +2838,14 @@ "tags": [ "players" ], - "summary": "Retrieves the related groups of an individual player's groups relationship without returning them.", + "summary": "Retrieves the related games of an individual player's ownedGames 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": "headPlayerGroups", + "operationId": "headPlayerOwnedGames", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related groups to retrieve.", + "description": "The identifier of the player whose related games to retrieve.", "required": true, "schema": { "type": "string" @@ -2915,18 +2915,18 @@ } } }, - "/players/{id}/relationships/groups": { + "/players/{id}/relationships/ownedGames": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related group identities of an individual player's groups relationship.", - "operationId": "getPlayerGroupsRelationship", + "summary": "Retrieves the related game identities of an individual player's ownedGames relationship.", + "operationId": "getPlayerOwnedGamesRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related group identities to retrieve.", + "description": "The identifier of the player whose related game identities to retrieve.", "required": true, "schema": { "type": "string" @@ -2956,7 +2956,7 @@ ], "responses": { "200": { - "description": "Successfully returns the found group identities, or an empty array if none were found.", + "description": "Successfully returns the found game identities, or an empty array if none were found.", "headers": { "ETag": { "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", @@ -2969,7 +2969,7 @@ "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/groupIdentifierCollectionResponseDocument" + "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" } } } @@ -3012,14 +3012,14 @@ "tags": [ "players" ], - "summary": "Retrieves the related group identities of an individual player's groups relationship without returning them.", + "summary": "Retrieves the related game identities of an individual player's ownedGames 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": "headPlayerGroupsRelationship", + "operationId": "headPlayerOwnedGamesRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose related group identities to retrieve.", + "description": "The identifier of the player whose related game identities to retrieve.", "required": true, "schema": { "type": "string" @@ -3092,13 +3092,13 @@ "tags": [ "players" ], - "summary": "Adds existing groups to the groups relationship of an individual player.", - "operationId": "postPlayerGroupsRelationship", + "summary": "Adds existing games to the ownedGames relationship of an individual player.", + "operationId": "postPlayerOwnedGamesRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to add groups to.", + "description": "The identifier of the player to add games to.", "required": true, "schema": { "type": "string" @@ -3106,13 +3106,13 @@ } ], "requestBody": { - "description": "The identities of the groups to add to the groups relationship.", + "description": "The identities of the games to add to the ownedGames relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInRequest" + "$ref": "#/components/schemas/toManyGameInRequest" } ] } @@ -3121,7 +3121,7 @@ }, "responses": { "204": { - "description": "The groups were successfully added, which did not result in additional changes." + "description": "The games were successfully added, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -3159,13 +3159,13 @@ "tags": [ "players" ], - "summary": "Assigns existing groups to the groups relationship of an individual player.", - "operationId": "patchPlayerGroupsRelationship", + "summary": "Assigns existing games to the ownedGames relationship of an individual player.", + "operationId": "patchPlayerOwnedGamesRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose groups relationship to assign.", + "description": "The identifier of the player whose ownedGames relationship to assign.", "required": true, "schema": { "type": "string" @@ -3173,13 +3173,13 @@ } ], "requestBody": { - "description": "The identities of the groups to assign to the groups relationship, or an empty array to clear the relationship.", + "description": "The identities of the games to assign to the ownedGames relationship, or an empty array to clear the relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInRequest" + "$ref": "#/components/schemas/toManyGameInRequest" } ] } @@ -3188,7 +3188,7 @@ }, "responses": { "204": { - "description": "The groups relationship was successfully updated, which did not result in additional changes." + "description": "The ownedGames relationship was successfully updated, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -3226,13 +3226,13 @@ "tags": [ "players" ], - "summary": "Removes existing groups from the groups relationship of an individual player.", - "operationId": "deletePlayerGroupsRelationship", + "summary": "Removes existing games from the ownedGames relationship of an individual player.", + "operationId": "deletePlayerOwnedGamesRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player to remove groups from.", + "description": "The identifier of the player to remove games from.", "required": true, "schema": { "type": "string" @@ -3240,13 +3240,13 @@ } ], "requestBody": { - "description": "The identities of the groups to remove from the groups relationship.", + "description": "The identities of the games to remove from the ownedGames relationship.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInRequest" + "$ref": "#/components/schemas/toManyGameInRequest" } ] } @@ -3255,7 +3255,7 @@ }, "responses": { "204": { - "description": "The groups were successfully removed, which did not result in additional changes." + "description": "The games were successfully removed, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -3314,7 +3314,7 @@ "propertyName": "type", "mapping": { "games": "#/components/schemas/gameDataInResponse", - "groups": "#/components/schemas/groupDataInResponse", + "playerGroups": "#/components/schemas/playerGroupDataInResponse", "players": "#/components/schemas/playerDataInResponse" } }, @@ -3416,10 +3416,10 @@ "gameAttributesInPatchRequest": { "type": "object", "properties": { - "name": { + "title": { "type": "string" }, - "price": { + "purchasePrice": { "type": "number", "format": "double" } @@ -3428,14 +3428,14 @@ }, "gameAttributesInPostRequest": { "required": [ - "name" + "title" ], "type": "object", "properties": { - "name": { + "title": { "type": "string" }, - "price": { + "purchasePrice": { "type": "number", "format": "double" } @@ -3445,10 +3445,10 @@ "gameAttributesInResponse": { "type": "object", "properties": { - "name": { + "title": { "type": "string" }, - "price": { + "purchasePrice": { "type": "number", "format": "double" } @@ -3699,37 +3699,145 @@ "type": "string", "additionalProperties": false }, - "groupAttributesInPatchRequest": { + "linksInRelationship": { + "required": [ + "related", + "self" + ], "type": "object", "properties": { - "name": { + "self": { + "minLength": 1, + "type": "string" + }, + "related": { + "minLength": 1, "type": "string" } }, "additionalProperties": false }, - "groupAttributesInPostRequest": { + "linksInResourceCollectionDocument": { "required": [ - "name" + "self" ], "type": "object", "properties": { - "name": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { + "type": "string" + }, + "first": { + "type": "string" + }, + "last": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "next": { "type": "string" } }, "additionalProperties": false }, - "groupAttributesInResponse": { + "linksInResourceData": { + "required": [ + "self" + ], "type": "object", "properties": { - "name": { + "self": { + "minLength": 1, + "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 + }, + "playerAttributesInPatchRequest": { + "type": "object", + "properties": { + "userName": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerAttributesInPostRequest": { + "required": [ + "userName" + ], + "type": "object", + "properties": { + "userName": { "type": "string" } }, "additionalProperties": false }, - "groupCollectionResponseDocument": { + "playerAttributesInResponse": { + "type": "object", + "properties": { + "userName": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerCollectionResponseDocument": { "required": [ "data", "links" @@ -3746,7 +3854,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/groupDataInResponse" + "$ref": "#/components/schemas/playerDataInResponse" } }, "included": { @@ -3765,7 +3873,7 @@ }, "additionalProperties": false }, - "groupDataInPatchRequest": { + "playerDataInPatchRequest": { "required": [ "id", "type" @@ -3773,7 +3881,7 @@ "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/groupResourceType" + "$ref": "#/components/schemas/playerResourceType" }, "id": { "minLength": 1, @@ -3782,47 +3890,52 @@ "attributes": { "allOf": [ { - "$ref": "#/components/schemas/groupAttributesInPatchRequest" + "$ref": "#/components/schemas/playerAttributesInPatchRequest" } ] }, "relationships": { "allOf": [ { - "$ref": "#/components/schemas/groupRelationshipsInPatchRequest" + "$ref": "#/components/schemas/playerRelationshipsInPatchRequest" } ] } }, "additionalProperties": false }, - "groupDataInPostRequest": { + "playerDataInPostRequest": { "required": [ + "id", "type" ], "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/groupResourceType" + "$ref": "#/components/schemas/playerResourceType" + }, + "id": { + "minLength": 1, + "type": "string" }, "attributes": { "allOf": [ { - "$ref": "#/components/schemas/groupAttributesInPostRequest" + "$ref": "#/components/schemas/playerAttributesInPostRequest" } ] }, "relationships": { "allOf": [ { - "$ref": "#/components/schemas/groupRelationshipsInPostRequest" + "$ref": "#/components/schemas/playerRelationshipsInPostRequest" } ] } }, "additionalProperties": false }, - "groupDataInResponse": { + "playerDataInResponse": { "allOf": [ { "$ref": "#/components/schemas/dataInResponse" @@ -3836,14 +3949,14 @@ "attributes": { "allOf": [ { - "$ref": "#/components/schemas/groupAttributesInResponse" + "$ref": "#/components/schemas/playerAttributesInResponse" } ] }, "relationships": { "allOf": [ { - "$ref": "#/components/schemas/groupRelationshipsInResponse" + "$ref": "#/components/schemas/playerRelationshipsInResponse" } ] }, @@ -3867,24 +3980,37 @@ ], "additionalProperties": false }, - "groupIdentifier": { + "playerGroupAttributesInPatchRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerGroupAttributesInPostRequest": { "required": [ - "id", - "type" + "name" ], "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/groupResourceType" - }, - "id": { - "minLength": 1, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "playerGroupAttributesInResponse": { + "type": "object", + "properties": { + "name": { "type": "string" } }, "additionalProperties": false }, - "groupIdentifierCollectionResponseDocument": { + "playerGroupCollectionResponseDocument": { "required": [ "data", "links" @@ -3894,14 +4020,20 @@ "links": { "allOf": [ { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + "$ref": "#/components/schemas/linksInResourceCollectionDocument" } ] }, "data": { "type": "array", "items": { - "$ref": "#/components/schemas/groupIdentifier" + "$ref": "#/components/schemas/playerGroupDataInResponse" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataInResponse" } }, "meta": { @@ -3914,260 +4046,188 @@ }, "additionalProperties": false }, - "groupPatchRequestDocument": { + "playerGroupDataInPatchRequest": { "required": [ - "data" + "id", + "type" ], "type": "object", "properties": { - "data": { + "type": { + "$ref": "#/components/schemas/playerGroupResourceType" + }, + "id": { + "minLength": 1, + "type": "string" + }, + "attributes": { "allOf": [ { - "$ref": "#/components/schemas/groupDataInPatchRequest" + "$ref": "#/components/schemas/playerGroupAttributesInPatchRequest" } ] - } - }, - "additionalProperties": false - }, - "groupPostRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { + }, + "relationships": { "allOf": [ { - "$ref": "#/components/schemas/groupDataInPostRequest" + "$ref": "#/components/schemas/playerGroupRelationshipsInPatchRequest" } ] } }, "additionalProperties": false }, - "groupPrimaryResponseDocument": { + "playerGroupDataInPostRequest": { "required": [ - "data", - "links" + "type" ], "type": "object", "properties": { - "links": { + "type": { + "$ref": "#/components/schemas/playerGroupResourceType" + }, + "attributes": { "allOf": [ { - "$ref": "#/components/schemas/linksInResourceDocument" + "$ref": "#/components/schemas/playerGroupAttributesInPostRequest" } ] }, - "data": { + "relationships": { "allOf": [ { - "$ref": "#/components/schemas/groupDataInResponse" + "$ref": "#/components/schemas/playerGroupRelationshipsInPostRequest" } ] - }, - "included": { - "type": "array", - "items": { - "$ref": "#/components/schemas/dataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } } }, "additionalProperties": false }, - "groupRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "players": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - }, - "additionalProperties": false - }, - "groupRelationshipsInPostRequest": { - "type": "object", - "properties": { - "players": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - }, - "additionalProperties": false - }, - "groupRelationshipsInResponse": { - "type": "object", - "properties": { - "players": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInResponse" + "playerGroupDataInResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/dataInResponse" + }, + { + "required": [ + "links" + ], + "type": "object", + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/components/schemas/playerGroupAttributesInResponse" + } + ] + }, + "relationships": { + "allOf": [ + { + "$ref": "#/components/schemas/playerGroupRelationshipsInResponse" + } + ] + }, + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceData" + } + ] + }, + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } } - ] + }, + "additionalProperties": false } - }, - "additionalProperties": false - }, - "groupResourceType": { - "enum": [ - "groups" ], - "type": "string", "additionalProperties": false }, - "linksInRelationship": { + "playerGroupIdentifier": { "required": [ - "related", - "self" + "id", + "type" ], "type": "object", "properties": { - "self": { - "minLength": 1, - "type": "string" + "type": { + "$ref": "#/components/schemas/playerGroupResourceType" }, - "related": { + "id": { "minLength": 1, "type": "string" } }, "additionalProperties": false }, - "linksInResourceCollectionDocument": { + "playerGroupIdentifierCollectionResponseDocument": { "required": [ - "self" + "data", + "links" ], "type": "object", "properties": { - "self": { - "minLength": 1, - "type": "string" - }, - "describedby": { - "type": "string" - }, - "first": { - "type": "string" - }, - "last": { - "type": "string" - }, - "prev": { - "type": "string" + "links": { + "allOf": [ + { + "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" + } + ] }, - "next": { - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceData": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - }, - "linksInResourceDocument": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "type": "string" + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/playerGroupIdentifier" + } }, - "describedby": { - "type": "string" + "meta": { + "type": "object", + "additionalProperties": { + "type": "object", + "nullable": true + } } }, "additionalProperties": false }, - "linksInResourceIdentifierCollectionDocument": { + "playerGroupPatchRequestDocument": { "required": [ - "related", - "self" + "data" ], "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 - }, - "playerAttributesInPatchRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/playerGroupDataInPatchRequest" + } + ] } }, "additionalProperties": false }, - "playerAttributesInPostRequest": { + "playerGroupPostRequestDocument": { "required": [ - "name" + "data" ], "type": "object", "properties": { - "name": { - "type": "string" - } - }, - "additionalProperties": false - }, - "playerAttributesInResponse": { - "type": "object", - "properties": { - "name": { - "type": "string" + "data": { + "allOf": [ + { + "$ref": "#/components/schemas/playerGroupDataInPostRequest" + } + ] } }, "additionalProperties": false }, - "playerCollectionResponseDocument": { + "playerGroupPrimaryResponseDocument": { "required": [ "data", "links" @@ -4177,15 +4237,16 @@ "links": { "allOf": [ { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" + "$ref": "#/components/schemas/linksInResourceDocument" } ] }, "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/playerDataInResponse" - } + "allOf": [ + { + "$ref": "#/components/schemas/playerGroupDataInResponse" + } + ] }, "included": { "type": "array", @@ -4203,111 +4264,50 @@ }, "additionalProperties": false }, - "playerDataInPatchRequest": { - "required": [ - "id", - "type" - ], + "playerGroupRelationshipsInPatchRequest": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/playerResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "allOf": [ - { - "$ref": "#/components/schemas/playerAttributesInPatchRequest" - } - ] - }, - "relationships": { + "players": { "allOf": [ { - "$ref": "#/components/schemas/playerRelationshipsInPatchRequest" + "$ref": "#/components/schemas/toManyPlayerInRequest" } ] } }, "additionalProperties": false }, - "playerDataInPostRequest": { - "required": [ - "id", - "type" - ], + "playerGroupRelationshipsInPostRequest": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/playerResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { + "players": { "allOf": [ { - "$ref": "#/components/schemas/playerAttributesInPostRequest" + "$ref": "#/components/schemas/toManyPlayerInRequest" } ] - }, - "relationships": { + } + }, + "additionalProperties": false + }, + "playerGroupRelationshipsInResponse": { + "type": "object", + "properties": { + "players": { "allOf": [ { - "$ref": "#/components/schemas/playerRelationshipsInPostRequest" + "$ref": "#/components/schemas/toManyPlayerInResponse" } ] } }, "additionalProperties": false }, - "playerDataInResponse": { - "allOf": [ - { - "$ref": "#/components/schemas/dataInResponse" - }, - { - "required": [ - "links" - ], - "type": "object", - "properties": { - "attributes": { - "allOf": [ - { - "$ref": "#/components/schemas/playerAttributesInResponse" - } - ] - }, - "relationships": { - "allOf": [ - { - "$ref": "#/components/schemas/playerRelationshipsInResponse" - } - ] - }, - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceData" - } - ] - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - } + "playerGroupResourceType": { + "enum": [ + "playerGroups" ], + "type": "string", "additionalProperties": false }, "playerIdentifier": { @@ -4429,17 +4429,17 @@ "playerRelationshipsInPatchRequest": { "type": "object", "properties": { - "games": { + "ownedGames": { "allOf": [ { "$ref": "#/components/schemas/toManyGameInRequest" } ] }, - "groups": { + "groupMemberships": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInRequest" + "$ref": "#/components/schemas/toManyPlayerGroupInRequest" } ] } @@ -4449,17 +4449,17 @@ "playerRelationshipsInPostRequest": { "type": "object", "properties": { - "games": { + "ownedGames": { "allOf": [ { "$ref": "#/components/schemas/toManyGameInRequest" } ] }, - "groups": { + "groupMemberships": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInRequest" + "$ref": "#/components/schemas/toManyPlayerGroupInRequest" } ] } @@ -4469,17 +4469,17 @@ "playerRelationshipsInResponse": { "type": "object", "properties": { - "games": { + "ownedGames": { "allOf": [ { "$ref": "#/components/schemas/toManyGameInResponse" } ] }, - "groups": { + "groupMemberships": { "allOf": [ { - "$ref": "#/components/schemas/toManyGroupInResponse" + "$ref": "#/components/schemas/toManyPlayerGroupInResponse" } ] } @@ -4537,7 +4537,7 @@ }, "additionalProperties": false }, - "toManyGroupInRequest": { + "toManyPlayerGroupInRequest": { "required": [ "data" ], @@ -4546,13 +4546,13 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/groupIdentifier" + "$ref": "#/components/schemas/playerGroupIdentifier" } } }, "additionalProperties": false }, - "toManyGroupInResponse": { + "toManyPlayerGroupInResponse": { "required": [ "links" ], @@ -4568,7 +4568,7 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/groupIdentifier" + "$ref": "#/components/schemas/playerGroupIdentifier" } }, "meta": { diff --git a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj index 17636ff293..7708d23d78 100644 --- a/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj +++ b/test/OpenApiEndToEndTests/OpenApiEndToEndTests.csproj @@ -23,10 +23,10 @@ - - OpenApiEndToEndTests.ClientGeneratedId.GeneratedCode - ClientGeneratedIdClient - ClientGeneratedIdClient.cs + + OpenApiEndToEndTests.ClientIdGenerationModes.GeneratedCode + ClientIdGenerationModesClient + ClientIdGenerationModesClient.cs NSwagCSharp /ClientClassAccessModifier:internal /GenerateExceptionClasses:false /AdditionalNamespaceUsages:JsonApiDotNetCore.OpenApi.Client /GenerateNullableReferenceTypes:true diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs b/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs deleted file mode 100644 index 6ea2f56513..0000000000 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdDbContext.cs +++ /dev/null @@ -1,13 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; -using TestBuildingBlocks; - -namespace OpenApiTests.ClientGeneratedId; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ClientGeneratedIdDbContext(DbContextOptions options) : TestableDbContext(options) -{ - public DbSet Players => Set(); - public DbSet Games => Set(); - public DbSet Groups => Set(); -} diff --git a/test/OpenApiTests/ClientGeneratedId/Player.cs b/test/OpenApiTests/ClientGeneratedId/Player.cs deleted file mode 100644 index 610298a80d..0000000000 --- a/test/OpenApiTests/ClientGeneratedId/Player.cs +++ /dev/null @@ -1,20 +0,0 @@ -using JetBrains.Annotations; -using JsonApiDotNetCore.Configuration; -using JsonApiDotNetCore.Resources; -using JsonApiDotNetCore.Resources.Annotations; - -namespace OpenApiTests.ClientGeneratedId; - -[UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Required)] -public sealed class Player : Identifiable -{ - [Attr] - public string Name { get; set; } = null!; - - [HasMany] - public List Games { get; set; } = []; - - [HasMany] - public List Groups { get; set; } = []; -} diff --git a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs new file mode 100644 index 0000000000..e240ac77bc --- /dev/null +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.ClientIdGenerationModes; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class ClientIdGenerationModesDbContext(DbContextOptions options) : TestableDbContext(options) +{ + public DbSet Players => Set(); + public DbSet Games => Set(); + public DbSet Groups => Set(); +} diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs similarity index 50% rename from test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs rename to test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs index 5f14dc74a7..445b87a4a5 100644 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdFakers.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs @@ -5,25 +5,25 @@ // @formatter:wrap_chained_method_calls chop_if_long // @formatter:wrap_before_first_method_call true -namespace OpenApiTests.ClientGeneratedId; +namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -public sealed class ClientGeneratedIdFakers : FakerContainer +public sealed class ClientIdGenerationModesFakers : FakerContainer { private readonly Lazy> _lazyPlayerFaker = new(() => new Faker() .UseSeed(GetFakerSeed()) - .RuleFor(player => player.Name, faker => faker.Person.UserName)); + .RuleFor(player => player.UserName, faker => faker.Person.UserName)); private readonly Lazy> _lazyGameFaker = new(() => new Faker() .UseSeed(GetFakerSeed()) - .RuleFor(player => player.Name, faker => faker.Commerce.ProductName()) - .RuleFor(player => player.Price, faker => decimal.Parse(faker.Commerce.Price()))); + .RuleFor(game => game.Title, faker => faker.Commerce.ProductName()) + .RuleFor(game => game.PurchasePrice, faker => decimal.Parse(faker.Commerce.Price()))); - private readonly Lazy> _lazyGroupFaker = new(() => new Faker() + private readonly Lazy> _lazyGroupFaker = new(() => new Faker() .UseSeed(GetFakerSeed()) - .RuleFor(group => group.Name, faker => faker.Person.Company.Name)); + .RuleFor(playerGroup => playerGroup.Name, faker => faker.Person.Company.Name)); public Faker Player => _lazyPlayerFaker.Value; public Faker Game => _lazyGameFaker.Value; - public Faker Group => _lazyGroupFaker.Value; + public Faker Group => _lazyGroupFaker.Value; } diff --git a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs similarity index 50% rename from test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs rename to test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs index 6b15fa0167..fb52168a94 100644 --- a/test/OpenApiTests/ClientGeneratedId/ClientGeneratedIdTests.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs @@ -2,21 +2,22 @@ using TestBuildingBlocks; using Xunit; -namespace OpenApiTests.ClientGeneratedId; +namespace OpenApiTests.ClientIdGenerationModes; -public sealed class ClientGeneratedIdTests : IClassFixture, ClientGeneratedIdDbContext>> +public sealed class ClientIdGenerationModesTests + : IClassFixture, ClientIdGenerationModesDbContext>> { - private readonly OpenApiTestContext, ClientGeneratedIdDbContext> _testContext; + private readonly OpenApiTestContext, ClientIdGenerationModesDbContext> _testContext; - public ClientGeneratedIdTests(OpenApiTestContext, ClientGeneratedIdDbContext> testContext) + public ClientIdGenerationModesTests(OpenApiTestContext, ClientIdGenerationModesDbContext> testContext) { _testContext = testContext; testContext.UseController(); testContext.UseController(); - testContext.UseController(); + testContext.UseController(); - testContext.SwaggerDocumentOutputDirectory = "test/OpenApiEndToEndTests/ClientGeneratedId"; + testContext.SwaggerDocumentOutputDirectory = "test/OpenApiEndToEndTests/ClientIdGenerationModes"; } [Fact] @@ -26,14 +27,14 @@ public async Task Post_data_should_have_required_id() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.Should().ContainPath("components.schemas.playerDataInPostRequest").With(resourceDataInPostRequestElement => + document.Should().ContainPath("components.schemas.playerDataInPostRequest").With(dataElement => { - resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + dataElement.Should().ContainPath("required").With(requiredElement => { requiredElement.Should().ContainArrayElement("id"); }); - resourceDataInPostRequestElement.Should().ContainPath("properties.id"); + dataElement.Should().ContainPath("properties.id"); }); } @@ -44,14 +45,14 @@ public async Task Post_data_should_have_non_required_id() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.Should().ContainPath("components.schemas.gameDataInPostRequest").With(resourceDataInPostRequestElement => + document.Should().ContainPath("components.schemas.gameDataInPostRequest").With(dataElement => { - resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + dataElement.Should().ContainPath("required").With(requiredElement => { requiredElement.Should().NotContainArrayElement("id"); }); - resourceDataInPostRequestElement.Should().ContainPath("properties.id"); + dataElement.Should().ContainPath("properties.id"); }); } @@ -62,14 +63,14 @@ public async Task Post_data_should_not_have_id() JsonElement document = await _testContext.GetSwaggerDocumentAsync(); // Assert - document.Should().ContainPath("components.schemas.groupDataInPostRequest").With(resourceDataInPostRequestElement => + document.Should().ContainPath("components.schemas.playerGroupDataInPostRequest").With(dataElement => { - resourceDataInPostRequestElement.Should().ContainPath("required").With(requiredElement => + dataElement.Should().ContainPath("required").With(requiredElement => { requiredElement.Should().NotContainArrayElement("id"); }); - resourceDataInPostRequestElement.Should().NotContainPath("properties.id"); + dataElement.Should().NotContainPath("properties.id"); }); } } diff --git a/test/OpenApiTests/ClientGeneratedId/Game.cs b/test/OpenApiTests/ClientIdGenerationModes/Game.cs similarity index 50% rename from test/OpenApiTests/ClientGeneratedId/Game.cs rename to test/OpenApiTests/ClientIdGenerationModes/Game.cs index ca8c4fc36b..bf1cbc880e 100644 --- a/test/OpenApiTests/ClientGeneratedId/Game.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/Game.cs @@ -3,15 +3,15 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ClientGeneratedId; +namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Allowed)] +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Allowed)] public sealed class Game : Identifiable { [Attr] - public string Name { get; set; } = null!; + public string Title { get; set; } = null!; [Attr] - public decimal Price { get; set; } + public decimal PurchasePrice { get; set; } } diff --git a/test/OpenApiTests/ClientIdGenerationModes/Player.cs b/test/OpenApiTests/ClientIdGenerationModes/Player.cs new file mode 100644 index 0000000000..c1438d4396 --- /dev/null +++ b/test/OpenApiTests/ClientIdGenerationModes/Player.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.ClientIdGenerationModes; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Required)] +public sealed class Player : Identifiable +{ + [Attr] + public string UserName { get; set; } = null!; + + [HasMany] + public List OwnedGames { get; set; } = []; + + [HasMany] + public List GroupMemberships { get; set; } = []; +} diff --git a/test/OpenApiTests/ClientGeneratedId/Group.cs b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs similarity index 58% rename from test/OpenApiTests/ClientGeneratedId/Group.cs rename to test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs index f1b27dca3f..663f919a98 100644 --- a/test/OpenApiTests/ClientGeneratedId/Group.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs @@ -3,11 +3,11 @@ using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; -namespace OpenApiTests.ClientGeneratedId; +namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientGeneratedId", ClientIdGeneration = ClientIdGenerationMode.Forbidden)] -public sealed class Group : Identifiable +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Forbidden)] +public sealed class PlayerGroup : Identifiable { [Attr] public string Name { get; set; } = null!; From 8c8f95098adcaac2aa9d3c142a9fa689f04338ff Mon Sep 17 00:00:00 2001 From: verdie-g Date: Sun, 25 Feb 2024 23:31:52 -0500 Subject: [PATCH 4/6] Address PR comments --- ...sts.cs => ClientIdGenerationModesTests.cs} | 70 +++++++++++++------ .../ClientIdGenerationModes/swagger.g.json | 48 ++++++------- ...cs => ClientIdGenerationModesDbContext.cs} | 2 +- ...rs.cs => ClientIdGenerationModesFakers.cs} | 2 +- ...sts.cs => ClientIdGenerationModesTests.cs} | 6 +- .../ClientIdGenerationModes/Player.cs | 2 +- .../ClientIdGenerationModes/PlayerGroup.cs | 2 +- 7 files changed, 79 insertions(+), 53 deletions(-) rename test/OpenApiEndToEndTests/ClientIdGenerationModes/{PostTests.cs => ClientIdGenerationModesTests.cs} (60%) rename test/OpenApiTests/ClientIdGenerationModes/{ClientGeneratedIdDbContext.cs => ClientIdGenerationModesDbContext.cs} (86%) rename test/OpenApiTests/ClientIdGenerationModes/{ClientGeneratedIdFakers.cs => ClientIdGenerationModesFakers.cs} (92%) rename test/OpenApiTests/ClientIdGenerationModes/{ClientGeneratedIdTests.cs => ClientIdGenerationModesTests.cs} (91%) diff --git a/test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs b/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs similarity index 60% rename from test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs rename to test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs index 622451ac78..2e8fcbefb6 100644 --- a/test/OpenApiEndToEndTests/ClientIdGenerationModes/PostTests.cs +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs @@ -10,12 +10,13 @@ namespace OpenApiEndToEndTests.ClientIdGenerationModes; -public sealed class PostTests : IClassFixture, ClientIdGenerationModesDbContext>> +public sealed class ClientIdGenerationModesTests + : IClassFixture, ClientIdGenerationModesDbContext>> { private readonly IntegrationTestContext, ClientIdGenerationModesDbContext> _testContext; private readonly ClientIdGenerationModesFakers _fakers = new(); - public PostTests(IntegrationTestContext, ClientIdGenerationModesDbContext> testContext) + public ClientIdGenerationModesTests(IntegrationTestContext, ClientIdGenerationModesDbContext> testContext) { _testContext = testContext; @@ -25,7 +26,7 @@ public PostTests(IntegrationTestContext> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + PlayerPrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument { Data = new PlayerDataInPostRequest { @@ -75,12 +76,18 @@ public async Task Can_create_resource_with_ID_when_mode_is_required() })); // Assert - PlayerPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; doc.Should().BeNull(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + Player playerInDatabase = await dbContext.Players.FirstWithIdAsync(player.Id); + + playerInDatabase.UserName.Should().Be(player.UserName); + }); } [Fact] - public async Task Can_create_resource_without_ID_when_mode_is_allowed() + public async Task Can_create_resource_without_ID_when_supplying_ID_is_allowed() { // Arrange Game game = _fakers.Game.Generate(); @@ -89,7 +96,7 @@ public async Task Can_create_resource_without_ID_when_mode_is_allowed() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument + GamePrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { @@ -103,12 +110,19 @@ public async Task Can_create_resource_without_ID_when_mode_is_allowed() })); // Assert - GamePrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; doc?.Data.Id.Should().NotBeNullOrEmpty(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + Game gameInDatabase = await dbContext.Games.FirstWithIdAsync(Guid.Parse(doc!.Data.Id)); + + gameInDatabase.Title.Should().Be(game.Title); + gameInDatabase.PurchasePrice.Should().Be(game.PurchasePrice); + }); } [Fact] - public async Task Can_create_resource_with_ID_when_mode_is_allowed() + public async Task Can_create_resource_with_ID_when_supplying_ID_is_allowed() { // Arrange Game game = _fakers.Game.Generate(); @@ -118,7 +132,7 @@ public async Task Can_create_resource_with_ID_when_mode_is_allowed() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument + GamePrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { @@ -132,12 +146,19 @@ public async Task Can_create_resource_with_ID_when_mode_is_allowed() })); // Assert - GamePrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; doc.Should().BeNull(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + Game gameInDatabase = await dbContext.Games.FirstWithIdAsync(game.Id); + + gameInDatabase.Title.Should().Be(game.Title); + gameInDatabase.PurchasePrice.Should().Be(game.PurchasePrice); + }); } [Fact] - public async Task Can_create_resource_without_ID_when_mode_is_forbidden() + public async Task Can_create_resource_without_ID_when_supplying_ID_is_forbidden() { // Arrange PlayerGroup playerGroup = _fakers.Group.Generate(); @@ -146,20 +167,25 @@ public async Task Can_create_resource_without_ID_when_mode_is_forbidden() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - Func> action = () => ApiResponse.TranslateAsync(() => apiClient.PostPlayerGroupAsync(null, - new PlayerGroupPostRequestDocument + PlayerGroupPrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerGroupAsync(null, new PlayerGroupPostRequestDocument + { + Data = new PlayerGroupDataInPostRequest { - Data = new PlayerGroupDataInPostRequest + Attributes = new PlayerGroupAttributesInPostRequest { - Attributes = new PlayerGroupAttributesInPostRequest - { - Name = playerGroup.Name - } + Name = playerGroup.Name } - })); + } + })); // Assert - PlayerGroupPrimaryResponseDocument? doc = (await action.Should().NotThrowAsync()).Subject; doc?.Data.Id.Should().NotBeNullOrEmpty(); + + await _testContext.RunOnDatabaseAsync(async dbContext => + { + PlayerGroup playerGroupInDatabase = await dbContext.PlayerGroups.FirstWithIdAsync(long.Parse(doc!.Data.Id)); + + playerGroupInDatabase.Name.Should().Be(playerGroup.Name); + }); } } diff --git a/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json index 1258d1b713..45b8acf06a 100644 --- a/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json @@ -2192,13 +2192,13 @@ } } }, - "/players/{id}/groupMemberships": { + "/players/{id}/memberOf": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related playerGroups of an individual player's groupMemberships relationship.", - "operationId": "getPlayerGroupMemberships", + "summary": "Retrieves the related playerGroups of an individual player's memberOf relationship.", + "operationId": "getPlayerMemberOf", "parameters": [ { "name": "id", @@ -2289,9 +2289,9 @@ "tags": [ "players" ], - "summary": "Retrieves the related playerGroups of an individual player's groupMemberships relationship without returning them.", + "summary": "Retrieves the related playerGroups of an individual player's memberOf 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": "headPlayerGroupMemberships", + "operationId": "headPlayerMemberOf", "parameters": [ { "name": "id", @@ -2366,13 +2366,13 @@ } } }, - "/players/{id}/relationships/groupMemberships": { + "/players/{id}/relationships/memberOf": { "get": { "tags": [ "players" ], - "summary": "Retrieves the related playerGroup identities of an individual player's groupMemberships relationship.", - "operationId": "getPlayerGroupMembershipsRelationship", + "summary": "Retrieves the related playerGroup identities of an individual player's memberOf relationship.", + "operationId": "getPlayerMemberOfRelationship", "parameters": [ { "name": "id", @@ -2463,9 +2463,9 @@ "tags": [ "players" ], - "summary": "Retrieves the related playerGroup identities of an individual player's groupMemberships relationship without returning them.", + "summary": "Retrieves the related playerGroup identities of an individual player's memberOf 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": "headPlayerGroupMembershipsRelationship", + "operationId": "headPlayerMemberOfRelationship", "parameters": [ { "name": "id", @@ -2543,8 +2543,8 @@ "tags": [ "players" ], - "summary": "Adds existing playerGroups to the groupMemberships relationship of an individual player.", - "operationId": "postPlayerGroupMembershipsRelationship", + "summary": "Adds existing playerGroups to the memberOf relationship of an individual player.", + "operationId": "postPlayerMemberOfRelationship", "parameters": [ { "name": "id", @@ -2557,7 +2557,7 @@ } ], "requestBody": { - "description": "The identities of the playerGroups to add to the groupMemberships relationship.", + "description": "The identities of the playerGroups to add to the memberOf relationship.", "content": { "application/vnd.api+json": { "schema": { @@ -2610,13 +2610,13 @@ "tags": [ "players" ], - "summary": "Assigns existing playerGroups to the groupMemberships relationship of an individual player.", - "operationId": "patchPlayerGroupMembershipsRelationship", + "summary": "Assigns existing playerGroups to the memberOf relationship of an individual player.", + "operationId": "patchPlayerMemberOfRelationship", "parameters": [ { "name": "id", "in": "path", - "description": "The identifier of the player whose groupMemberships relationship to assign.", + "description": "The identifier of the player whose memberOf relationship to assign.", "required": true, "schema": { "type": "string" @@ -2624,7 +2624,7 @@ } ], "requestBody": { - "description": "The identities of the playerGroups to assign to the groupMemberships relationship, or an empty array to clear the relationship.", + "description": "The identities of the playerGroups to assign to the memberOf relationship, or an empty array to clear the relationship.", "content": { "application/vnd.api+json": { "schema": { @@ -2639,7 +2639,7 @@ }, "responses": { "204": { - "description": "The groupMemberships relationship was successfully updated, which did not result in additional changes." + "description": "The memberOf relationship was successfully updated, which did not result in additional changes." }, "400": { "description": "The request body is missing or malformed.", @@ -2677,8 +2677,8 @@ "tags": [ "players" ], - "summary": "Removes existing playerGroups from the groupMemberships relationship of an individual player.", - "operationId": "deletePlayerGroupMembershipsRelationship", + "summary": "Removes existing playerGroups from the memberOf relationship of an individual player.", + "operationId": "deletePlayerMemberOfRelationship", "parameters": [ { "name": "id", @@ -2691,7 +2691,7 @@ } ], "requestBody": { - "description": "The identities of the playerGroups to remove from the groupMemberships relationship.", + "description": "The identities of the playerGroups to remove from the memberOf relationship.", "content": { "application/vnd.api+json": { "schema": { @@ -4436,7 +4436,7 @@ } ] }, - "groupMemberships": { + "memberOf": { "allOf": [ { "$ref": "#/components/schemas/toManyPlayerGroupInRequest" @@ -4456,7 +4456,7 @@ } ] }, - "groupMemberships": { + "memberOf": { "allOf": [ { "$ref": "#/components/schemas/toManyPlayerGroupInRequest" @@ -4476,7 +4476,7 @@ } ] }, - "groupMemberships": { + "memberOf": { "allOf": [ { "$ref": "#/components/schemas/toManyPlayerGroupInResponse" diff --git a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesDbContext.cs similarity index 86% rename from test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs rename to test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesDbContext.cs index e240ac77bc..f346f95bcf 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdDbContext.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesDbContext.cs @@ -9,5 +9,5 @@ public sealed class ClientIdGenerationModesDbContext(DbContextOptions Players => Set(); public DbSet Games => Set(); - public DbSet Groups => Set(); + public DbSet PlayerGroups => Set(); } diff --git a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesFakers.cs similarity index 92% rename from test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs rename to test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesFakers.cs index 445b87a4a5..83c08c8a59 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdFakers.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesFakers.cs @@ -17,7 +17,7 @@ public sealed class ClientIdGenerationModesFakers : FakerContainer private readonly Lazy> _lazyGameFaker = new(() => new Faker() .UseSeed(GetFakerSeed()) .RuleFor(game => game.Title, faker => faker.Commerce.ProductName()) - .RuleFor(game => game.PurchasePrice, faker => decimal.Parse(faker.Commerce.Price()))); + .RuleFor(game => game.PurchasePrice, faker => faker.Finance.Amount(1, 80))); private readonly Lazy> _lazyGroupFaker = new(() => new Faker() .UseSeed(GetFakerSeed()) diff --git a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs similarity index 91% rename from test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs rename to test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs index fb52168a94..3b6d0d0f6e 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/ClientGeneratedIdTests.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs @@ -21,7 +21,7 @@ public ClientIdGenerationModesTests(OpenApiTestContext public List OwnedGames { get; set; } = []; [HasMany] - public List GroupMemberships { get; set; } = []; + public List MemberOf { get; set; } = []; } diff --git a/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs index 663f919a98..57ccccd8f0 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs @@ -7,7 +7,7 @@ namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] [Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Forbidden)] -public sealed class PlayerGroup : Identifiable +public sealed class PlayerGroup : Identifiable { [Attr] public string Name { get; set; } = null!; From bf41287350a8b5f3fead480ee69e4c19dd69b31a Mon Sep 17 00:00:00 2001 From: verdie-g Date: Sun, 25 Feb 2024 23:42:35 -0500 Subject: [PATCH 5/6] Only generate post endpoints --- .../ClientIdGenerationModes/swagger.g.json | 3634 +---------------- .../ClientIdGenerationModes/Game.cs | 4 +- .../ClientIdGenerationModes/Player.cs | 4 +- .../ClientIdGenerationModes/PlayerGroup.cs | 4 +- 4 files changed, 102 insertions(+), 3544 deletions(-) diff --git a/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json index 45b8acf06a..9583a743b6 100644 --- a/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/swagger.g.json @@ -11,147 +11,6 @@ ], "paths": { "/games": { - "get": { - "tags": [ - "games" - ], - "summary": "Retrieves a collection of games.", - "operationId": "getGameCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found games, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/gameCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "games" - ], - "summary": "Retrieves a collection of games 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": "headGameCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - } - } - }, "post": { "tags": [ "games" @@ -244,195 +103,14 @@ } } }, - "/games/{id}": { - "get": { - "tags": [ - "games" - ], - "summary": "Retrieves an individual game by its identifier.", - "operationId": "getGame", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the game to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found game.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/gamePrimaryResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The game does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "games" - ], - "summary": "Retrieves an individual game 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": "headGame", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the game to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The game does not exist." - } - } - }, - "patch": { + "/playerGroups": { + "post": { "tags": [ - "games" + "playerGroups" ], - "summary": "Updates an existing game.", - "operationId": "patchGame", + "summary": "Creates a new playerGroup.", + "operationId": "postPlayerGroup", "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the game to update.", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "query", "in": "query", @@ -448,13 +126,13 @@ } ], "requestBody": { - "description": "The attributes and relationships of the game to update. Omitted fields are left unchanged.", + "description": "The attributes and relationships of the playerGroup to create.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/gamePatchRequestDocument" + "$ref": "#/components/schemas/playerGroupPostRequestDocument" } ] } @@ -462,18 +140,28 @@ } }, "responses": { - "200": { - "description": "The game was successfully updated, which resulted in additional changes. The updated game is returned.", + "201": { + "description": "The playerGroup was successfully created, which resulted in additional changes. The newly created playerGroup is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created playerGroup can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, "content": { "application/vnd.api+json": { "schema": { - "$ref": "#/components/schemas/gamePrimaryResponseDocument" + "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" } } } }, "204": { - "description": "The game was successfully updated, which did not result in additional changes." + "description": "The playerGroup 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.", @@ -482,2716 +170,11 @@ "schema": { "$ref": "#/components/schemas/errorResponseDocument" } - } - } - }, - "404": { - "description": "The game or a related resource does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type or identifier in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "422": { - "description": "Validation of the request body failed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "delete": { - "tags": [ - "games" - ], - "summary": "Deletes an existing game by its identifier.", - "operationId": "deleteGame", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the game to delete.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "The game was successfully deleted." - }, - "404": { - "description": "The game does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/playerGroups": { - "get": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves a collection of playerGroups.", - "operationId": "getPlayerGroupCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found playerGroups, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves a collection of playerGroups 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": "headPlayerGroupCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - } - } - }, - "post": { - "tags": [ - "playerGroups" - ], - "summary": "Creates a new playerGroup.", - "operationId": "postPlayerGroup", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - } - ], - "requestBody": { - "description": "The attributes and relationships of the playerGroup to create.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/playerGroupPostRequestDocument" - } - ] - } - } - } - }, - "responses": { - "201": { - "description": "The playerGroup was successfully created, which resulted in additional changes. The newly created playerGroup is returned.", - "headers": { - "Location": { - "description": "The URL at which the newly created playerGroup can be retrieved.", - "required": true, - "schema": { - "type": "string", - "format": "uri" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "The playerGroup was successfully created, which did not result in additional changes." - }, - "400": { - "description": "The query string is invalid or the request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "403": { - "description": "Client-generated IDs cannot be used at this endpoint.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "422": { - "description": "Validation of the request body failed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/playerGroups/{id}": { - "get": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves an individual playerGroup by its identifier.", - "operationId": "getPlayerGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found playerGroup.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves an individual playerGroup 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": "headPlayerGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The playerGroup does not exist." - } - } - }, - "patch": { - "tags": [ - "playerGroups" - ], - "summary": "Updates an existing playerGroup.", - "operationId": "patchPlayerGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to update.", - "required": true, - "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": "" - } - } - ], - "requestBody": { - "description": "The attributes and relationships of the playerGroup to update. Omitted fields are left unchanged.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/playerGroupPatchRequestDocument" - } - ] - } - } - } - }, - "responses": { - "200": { - "description": "The playerGroup was successfully updated, which resulted in additional changes. The updated playerGroup is returned.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "The playerGroup was successfully updated, which did not result in additional changes." - }, - "400": { - "description": "The query string is invalid or the request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup or a related resource does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type or identifier in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "422": { - "description": "Validation of the request body failed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "delete": { - "tags": [ - "playerGroups" - ], - "summary": "Deletes an existing playerGroup by its identifier.", - "operationId": "deletePlayerGroup", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to delete.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "The playerGroup was successfully deleted." - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/playerGroups/{id}/players": { - "get": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves the related players of an individual playerGroup's players relationship.", - "operationId": "getPlayerGroupPlayers", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup whose related players to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found players, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves the related players of an individual playerGroup's players 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": "headPlayerGroupPlayers", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup whose related players to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The playerGroup does not exist." - } - } - } - }, - "/playerGroups/{id}/relationships/players": { - "get": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves the related player identities of an individual playerGroup's players relationship.", - "operationId": "getPlayerGroupPlayersRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup whose related player identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found player identities, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerIdentifierCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "playerGroups" - ], - "summary": "Retrieves the related player identities of an individual playerGroup's players 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": "headPlayerGroupPlayersRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup whose related player identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The playerGroup does not exist." - } - } - }, - "post": { - "tags": [ - "playerGroups" - ], - "summary": "Adds existing players to the players relationship of an individual playerGroup.", - "operationId": "postPlayerGroupPlayersRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to add players to.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the players to add to the players relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The players were successfully added, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "playerGroups" - ], - "summary": "Assigns existing players to the players relationship of an individual playerGroup.", - "operationId": "patchPlayerGroupPlayersRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup whose players relationship to assign.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the players to assign to the players relationship, or an empty array to clear the relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The players relationship was successfully updated, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "delete": { - "tags": [ - "playerGroups" - ], - "summary": "Removes existing players from the players relationship of an individual playerGroup.", - "operationId": "deletePlayerGroupPlayersRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the playerGroup to remove players from.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the players to remove from the players relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The players were successfully removed, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The playerGroup does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/players": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves a collection of players.", - "operationId": "getPlayerCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found players, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves a collection of players 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": "headPlayerCollection", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - } - } - }, - "post": { - "tags": [ - "players" - ], - "summary": "Creates a new player.", - "operationId": "postPlayer", - "parameters": [ - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - } - ], - "requestBody": { - "description": "The attributes and relationships of the player to create.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/playerPostRequestDocument" - } - ] - } - } - } - }, - "responses": { - "201": { - "description": "The player was successfully created, which resulted in additional changes. The newly created player is returned.", - "headers": { - "Location": { - "description": "The URL at which the newly created player can be retrieved.", - "required": true, - "schema": { - "type": "string", - "format": "uri" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "The player was successfully created, which did not result in additional changes." - }, - "400": { - "description": "The query string is invalid or the request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "422": { - "description": "Validation of the request body failed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/players/{id}": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves an individual player by its identifier.", - "operationId": "getPlayer", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found player.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves an individual player 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": "headPlayer", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The player does not exist." - } - } - }, - "patch": { - "tags": [ - "players" - ], - "summary": "Updates an existing player.", - "operationId": "patchPlayer", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to update.", - "required": true, - "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": "" - } - } - ], - "requestBody": { - "description": "The attributes and relationships of the player to update. Omitted fields are left unchanged.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/playerPatchRequestDocument" - } - ] - } - } - } - }, - "responses": { - "200": { - "description": "The player was successfully updated, which resulted in additional changes. The updated player is returned.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerPrimaryResponseDocument" - } - } - } - }, - "204": { - "description": "The player was successfully updated, which did not result in additional changes." - }, - "400": { - "description": "The query string is invalid or the request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player or a related resource does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type or identifier in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "422": { - "description": "Validation of the request body failed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "delete": { - "tags": [ - "players" - ], - "summary": "Deletes an existing player by its identifier.", - "operationId": "deletePlayer", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to delete.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "The player was successfully deleted." - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/players/{id}/memberOf": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves the related playerGroups of an individual player's memberOf relationship.", - "operationId": "getPlayerMemberOf", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related playerGroups to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found playerGroups, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves the related playerGroups of an individual player's memberOf 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": "headPlayerMemberOf", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related playerGroups to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The player does not exist." - } - } - } - }, - "/players/{id}/relationships/memberOf": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves the related playerGroup identities of an individual player's memberOf relationship.", - "operationId": "getPlayerMemberOfRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related playerGroup identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found playerGroup identities, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/playerGroupIdentifierCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves the related playerGroup identities of an individual player's memberOf 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": "headPlayerMemberOfRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related playerGroup identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The player does not exist." - } - } - }, - "post": { - "tags": [ - "players" - ], - "summary": "Adds existing playerGroups to the memberOf relationship of an individual player.", - "operationId": "postPlayerMemberOfRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to add playerGroups to.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the playerGroups to add to the memberOf relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerGroupInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The playerGroups were successfully added, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "players" - ], - "summary": "Assigns existing playerGroups to the memberOf relationship of an individual player.", - "operationId": "patchPlayerMemberOfRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose memberOf relationship to assign.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the playerGroups to assign to the memberOf relationship, or an empty array to clear the relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerGroupInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The memberOf relationship was successfully updated, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "delete": { - "tags": [ - "players" - ], - "summary": "Removes existing playerGroups from the memberOf relationship of an individual player.", - "operationId": "deletePlayerMemberOfRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to remove playerGroups from.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the playerGroups to remove from the memberOf relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerGroupInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The playerGroups were successfully removed, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - } - }, - "/players/{id}/ownedGames": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves the related games of an individual player's ownedGames relationship.", - "operationId": "getPlayerOwnedGames", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related games to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found games, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/gameCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves the related games of an individual player's ownedGames 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": "headPlayerOwnedGames", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related games to retrieve.", - "required": true, - "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": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The player does not exist." - } - } - } - }, - "/players/{id}/relationships/ownedGames": { - "get": { - "tags": [ - "players" - ], - "summary": "Retrieves the related game identities of an individual player's ownedGames relationship.", - "operationId": "getPlayerOwnedGamesRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Successfully returns the found game identities, or an empty array if none were found.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - }, - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/gameIdentifierCollectionResponseDocument" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "head": { - "tags": [ - "players" - ], - "summary": "Retrieves the related game identities of an individual player's ownedGames 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": "headPlayerOwnedGamesRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose related game identities to retrieve.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "query", - "in": "query", - "description": "For syntax, see the documentation for the [`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "nullable": true - }, - "example": "" - } - }, - { - "name": "If-None-Match", - "in": "header", - "description": "A list of ETags, resulting in HTTP status 304 without a body, if one of them matches the current fingerprint.", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "The operation completed successfully.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - }, - "Content-Length": { - "description": "Size of the HTTP response body, in bytes.", - "required": true, - "schema": { - "type": "integer", - "format": "int64" - } - } - } - }, - "304": { - "description": "The fingerprint of the HTTP response matches one of the ETags from the incoming If-None-Match header.", - "headers": { - "ETag": { - "description": "A fingerprint of the HTTP response, which can be used in an If-None-Match header to only fetch changes.", - "required": true, - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "The query string is invalid." - }, - "404": { - "description": "The player does not exist." - } - } - }, - "post": { - "tags": [ - "players" - ], - "summary": "Adds existing games to the ownedGames relationship of an individual player.", - "operationId": "postPlayerOwnedGamesRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player to add games to.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the games to add to the ownedGames relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyGameInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The games were successfully added, which did not result in additional changes." - }, - "400": { - "description": "The request body is missing or malformed.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "404": { - "description": "The player does not exist.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - }, - "409": { - "description": "A resource type in the request body is incompatible.", - "content": { - "application/vnd.api+json": { - "schema": { - "$ref": "#/components/schemas/errorResponseDocument" - } - } - } - } - } - }, - "patch": { - "tags": [ - "players" - ], - "summary": "Assigns existing games to the ownedGames relationship of an individual player.", - "operationId": "patchPlayerOwnedGamesRelationship", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The identifier of the player whose ownedGames relationship to assign.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "description": "The identities of the games to assign to the ownedGames relationship, or an empty array to clear the relationship.", - "content": { - "application/vnd.api+json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyGameInRequest" - } - ] - } - } - } - }, - "responses": { - "204": { - "description": "The ownedGames relationship was successfully updated, which did not result in additional changes." + } + } }, - "400": { - "description": "The request body is missing or malformed.", + "403": { + "description": "Client-generated IDs cannot be used at this endpoint.", "content": { "application/vnd.api+json": { "schema": { @@ -3200,8 +183,8 @@ } } }, - "404": { - "description": "The player does not exist.", + "409": { + "description": "A resource type in the request body is incompatible.", "content": { "application/vnd.api+json": { "schema": { @@ -3210,8 +193,8 @@ } } }, - "409": { - "description": "A resource type in the request body is incompatible.", + "422": { + "description": "Validation of the request body failed.", "content": { "application/vnd.api+json": { "schema": { @@ -3221,32 +204,38 @@ } } } - }, - "delete": { + } + }, + "/players": { + "post": { "tags": [ "players" ], - "summary": "Removes existing games from the ownedGames relationship of an individual player.", - "operationId": "deletePlayerOwnedGamesRelationship", + "summary": "Creates a new player.", + "operationId": "postPlayer", "parameters": [ { - "name": "id", - "in": "path", - "description": "The identifier of the player to remove games from.", - "required": true, + "name": "query", + "in": "query", + "description": "For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.", "schema": { - "type": "string" + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + }, + "example": "" } } ], "requestBody": { - "description": "The identities of the games to remove from the ownedGames relationship.", + "description": "The attributes and relationships of the player to create.", "content": { "application/vnd.api+json": { "schema": { "allOf": [ { - "$ref": "#/components/schemas/toManyGameInRequest" + "$ref": "#/components/schemas/playerPostRequestDocument" } ] } @@ -3254,11 +243,31 @@ } }, "responses": { + "201": { + "description": "The player was successfully created, which resulted in additional changes. The newly created player is returned.", + "headers": { + "Location": { + "description": "The URL at which the newly created player can be retrieved.", + "required": true, + "schema": { + "type": "string", + "format": "uri" + } + } + }, + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/playerPrimaryResponseDocument" + } + } + } + }, "204": { - "description": "The games were successfully removed, which did not result in additional changes." + "description": "The player 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.", "content": { "application/vnd.api+json": { "schema": { @@ -3267,8 +276,8 @@ } } }, - "404": { - "description": "The player does not exist.", + "409": { + "description": "A resource type in the request body is incompatible.", "content": { "application/vnd.api+json": { "schema": { @@ -3277,8 +286,8 @@ } } }, - "409": { - "description": "A resource type in the request body is incompatible.", + "422": { + "description": "Validation of the request body failed.", "content": { "application/vnd.api+json": { "schema": { @@ -3413,19 +422,6 @@ }, "additionalProperties": false }, - "gameAttributesInPatchRequest": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "purchasePrice": { - "type": "number", - "format": "double" - } - }, - "additionalProperties": false - }, "gameAttributesInPostRequest": { "required": [ "title" @@ -3455,66 +451,6 @@ }, "additionalProperties": false }, - "gameCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/gameDataInResponse" - } - }, - "included": { - "type": "array", - "items": { - "$ref": "#/components/schemas/dataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - }, - "gameDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/gameResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "allOf": [ - { - "$ref": "#/components/schemas/gameAttributesInPatchRequest" - } - ] - } - }, - "additionalProperties": false - }, "gameDataInPostRequest": { "required": [ "type" @@ -3593,52 +529,6 @@ }, "additionalProperties": false }, - "gameIdentifierCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/gameIdentifier" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - }, - "gamePatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "allOf": [ - { - "$ref": "#/components/schemas/gameDataInPatchRequest" - } - ] - } - }, - "additionalProperties": false - }, "gamePostRequestDocument": { "required": [ "data" @@ -3710,196 +600,59 @@ "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 - }, - "linksInResourceData": { - "required": [ - "self" - ], - "type": "object", - "properties": { - "self": { - "minLength": 1, - "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 - }, - "playerAttributesInPatchRequest": { - "type": "object", - "properties": { - "userName": { + "related": { + "minLength": 1, "type": "string" } }, "additionalProperties": false }, - "playerAttributesInPostRequest": { + "linksInResourceData": { "required": [ - "userName" + "self" ], "type": "object", "properties": { - "userName": { + "self": { + "minLength": 1, "type": "string" } }, "additionalProperties": false }, - "playerAttributesInResponse": { + "linksInResourceDocument": { + "required": [ + "self" + ], "type": "object", "properties": { - "userName": { + "self": { + "minLength": 1, + "type": "string" + }, + "describedby": { "type": "string" } }, "additionalProperties": false }, - "playerCollectionResponseDocument": { + "playerAttributesInPostRequest": { "required": [ - "data", - "links" + "userName" ], "type": "object", "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/playerDataInResponse" - } - }, - "included": { - "type": "array", - "items": { - "$ref": "#/components/schemas/dataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } + "userName": { + "type": "string" } }, "additionalProperties": false }, - "playerDataInPatchRequest": { - "required": [ - "id", - "type" - ], + "playerAttributesInResponse": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/playerResourceType" - }, - "id": { - "minLength": 1, + "userName": { "type": "string" - }, - "attributes": { - "allOf": [ - { - "$ref": "#/components/schemas/playerAttributesInPatchRequest" - } - ] - }, - "relationships": { - "allOf": [ - { - "$ref": "#/components/schemas/playerRelationshipsInPatchRequest" - } - ] } }, "additionalProperties": false @@ -3980,15 +733,6 @@ ], "additionalProperties": false }, - "playerGroupAttributesInPatchRequest": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - }, - "additionalProperties": false - }, "playerGroupAttributesInPostRequest": { "required": [ "name" @@ -4010,73 +754,6 @@ }, "additionalProperties": false }, - "playerGroupCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/playerGroupDataInResponse" - } - }, - "included": { - "type": "array", - "items": { - "$ref": "#/components/schemas/dataInResponse" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - }, - "playerGroupDataInPatchRequest": { - "required": [ - "id", - "type" - ], - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/playerGroupResourceType" - }, - "id": { - "minLength": 1, - "type": "string" - }, - "attributes": { - "allOf": [ - { - "$ref": "#/components/schemas/playerGroupAttributesInPatchRequest" - } - ] - }, - "relationships": { - "allOf": [ - { - "$ref": "#/components/schemas/playerGroupRelationshipsInPatchRequest" - } - ] - } - }, - "additionalProperties": false - }, "playerGroupDataInPostRequest": { "required": [ "type" @@ -4165,52 +842,6 @@ }, "additionalProperties": false }, - "playerGroupIdentifierCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/playerGroupIdentifier" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - }, - "playerGroupPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "allOf": [ - { - "$ref": "#/components/schemas/playerGroupDataInPatchRequest" - } - ] - } - }, - "additionalProperties": false - }, "playerGroupPostRequestDocument": { "required": [ "data" @@ -4264,19 +895,6 @@ }, "additionalProperties": false }, - "playerGroupRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "players": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerInRequest" - } - ] - } - }, - "additionalProperties": false - }, "playerGroupRelationshipsInPostRequest": { "type": "object", "properties": { @@ -4327,52 +945,6 @@ }, "additionalProperties": false }, - "playerIdentifierCollectionResponseDocument": { - "required": [ - "data", - "links" - ], - "type": "object", - "properties": { - "links": { - "allOf": [ - { - "$ref": "#/components/schemas/linksInResourceIdentifierCollectionDocument" - } - ] - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/playerIdentifier" - } - }, - "meta": { - "type": "object", - "additionalProperties": { - "type": "object", - "nullable": true - } - } - }, - "additionalProperties": false - }, - "playerPatchRequestDocument": { - "required": [ - "data" - ], - "type": "object", - "properties": { - "data": { - "allOf": [ - { - "$ref": "#/components/schemas/playerDataInPatchRequest" - } - ] - } - }, - "additionalProperties": false - }, "playerPostRequestDocument": { "required": [ "data" @@ -4426,26 +998,6 @@ }, "additionalProperties": false }, - "playerRelationshipsInPatchRequest": { - "type": "object", - "properties": { - "ownedGames": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyGameInRequest" - } - ] - }, - "memberOf": { - "allOf": [ - { - "$ref": "#/components/schemas/toManyPlayerGroupInRequest" - } - ] - } - }, - "additionalProperties": false - }, "playerRelationshipsInPostRequest": { "type": "object", "properties": { diff --git a/test/OpenApiTests/ClientIdGenerationModes/Game.cs b/test/OpenApiTests/ClientIdGenerationModes/Game.cs index bf1cbc880e..c4880054c1 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/Game.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/Game.cs @@ -1,12 +1,14 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Allowed)] +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Allowed, + GenerateControllerEndpoints = JsonApiEndpoints.Post)] public sealed class Game : Identifiable { [Attr] diff --git a/test/OpenApiTests/ClientIdGenerationModes/Player.cs b/test/OpenApiTests/ClientIdGenerationModes/Player.cs index 612b170dac..e150af5756 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/Player.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/Player.cs @@ -1,12 +1,14 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Required)] +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Required, + GenerateControllerEndpoints = JsonApiEndpoints.Post)] public sealed class Player : Identifiable { [Attr] diff --git a/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs index 57ccccd8f0..9f2547a61f 100644 --- a/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs +++ b/test/OpenApiTests/ClientIdGenerationModes/PlayerGroup.cs @@ -1,12 +1,14 @@ using JetBrains.Annotations; using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; using JsonApiDotNetCore.Resources; using JsonApiDotNetCore.Resources.Annotations; namespace OpenApiTests.ClientIdGenerationModes; [UsedImplicitly(ImplicitUseTargetFlags.Members)] -[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Forbidden)] +[Resource(ControllerNamespace = "OpenApiTests.ClientIdGenerationModes", ClientIdGeneration = ClientIdGenerationMode.Forbidden, + GenerateControllerEndpoints = JsonApiEndpoints.Post)] public sealed class PlayerGroup : Identifiable { [Attr] From 8af7556faff9e1a6a979c61d8a19fac3517c09df Mon Sep 17 00:00:00 2001 From: verdie-g Date: Mon, 26 Feb 2024 18:23:18 -0500 Subject: [PATCH 6/6] Address PR comments --- .../ClientIdGenerationModesTests.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs b/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs index 2e8fcbefb6..747707f4f0 100644 --- a/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs +++ b/test/OpenApiEndToEndTests/ClientIdGenerationModes/ClientIdGenerationModesTests.cs @@ -63,7 +63,7 @@ public async Task Can_create_resource_with_ID_when_supplying_ID_is_required() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - PlayerPrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument + PlayerPrimaryResponseDocument? document = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerAsync(null, new PlayerPostRequestDocument { Data = new PlayerDataInPostRequest { @@ -76,7 +76,7 @@ public async Task Can_create_resource_with_ID_when_supplying_ID_is_required() })); // Assert - doc.Should().BeNull(); + document.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -96,7 +96,7 @@ public async Task Can_create_resource_without_ID_when_supplying_ID_is_allowed() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - GamePrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument + GamePrimaryResponseDocument? document = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { @@ -110,11 +110,12 @@ public async Task Can_create_resource_without_ID_when_supplying_ID_is_allowed() })); // Assert - doc?.Data.Id.Should().NotBeNullOrEmpty(); + document.ShouldNotBeNull(); + document.Data.Id.Should().NotBeNullOrEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { - Game gameInDatabase = await dbContext.Games.FirstWithIdAsync(Guid.Parse(doc!.Data.Id)); + Game gameInDatabase = await dbContext.Games.FirstWithIdAsync(Guid.Parse(document.Data.Id)); gameInDatabase.Title.Should().Be(game.Title); gameInDatabase.PurchasePrice.Should().Be(game.PurchasePrice); @@ -132,7 +133,7 @@ public async Task Can_create_resource_with_ID_when_supplying_ID_is_allowed() ClientIdGenerationModesClient apiClient = new(httpClient); // Act - GamePrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument + GamePrimaryResponseDocument? document = await ApiResponse.TranslateAsync(() => apiClient.PostGameAsync(null, new GamePostRequestDocument { Data = new GameDataInPostRequest { @@ -146,7 +147,7 @@ public async Task Can_create_resource_with_ID_when_supplying_ID_is_allowed() })); // Assert - doc.Should().BeNull(); + document.Should().BeNull(); await _testContext.RunOnDatabaseAsync(async dbContext => { @@ -167,23 +168,25 @@ public async Task Can_create_resource_without_ID_when_supplying_ID_is_forbidden( ClientIdGenerationModesClient apiClient = new(httpClient); // Act - PlayerGroupPrimaryResponseDocument? doc = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerGroupAsync(null, new PlayerGroupPostRequestDocument - { - Data = new PlayerGroupDataInPostRequest + PlayerGroupPrimaryResponseDocument? document = await ApiResponse.TranslateAsync(() => apiClient.PostPlayerGroupAsync(null, + new PlayerGroupPostRequestDocument { - Attributes = new PlayerGroupAttributesInPostRequest + Data = new PlayerGroupDataInPostRequest { - Name = playerGroup.Name + Attributes = new PlayerGroupAttributesInPostRequest + { + Name = playerGroup.Name + } } - } - })); + })); // Assert - doc?.Data.Id.Should().NotBeNullOrEmpty(); + document.ShouldNotBeNull(); + document.Data.Id.Should().NotBeNullOrEmpty(); await _testContext.RunOnDatabaseAsync(async dbContext => { - PlayerGroup playerGroupInDatabase = await dbContext.PlayerGroups.FirstWithIdAsync(long.Parse(doc!.Data.Id)); + PlayerGroup playerGroupInDatabase = await dbContext.PlayerGroups.FirstWithIdAsync(long.Parse(document.Data.Id)); playerGroupInDatabase.Name.Should().Be(playerGroup.Name); });