From 6ec3c2589673388240e8dc077a30fa9ba4bc4761 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Wed, 10 Apr 2024 08:04:07 +0200 Subject: [PATCH] fix(OpenApiType): Use anyOf instead of oneOf for conflicting types Signed-off-by: provokateurin --- src/OpenApiType.php | 25 ++- tests/appinfo/routes.php | 2 + tests/lib/Controller/SettingsController.php | 24 +++ tests/openapi-administration.json | 195 ++++++++++++++++++++ tests/openapi-full.json | 195 ++++++++++++++++++++ 5 files changed, 439 insertions(+), 2 deletions(-) diff --git a/src/OpenApiType.php b/src/OpenApiType.php index 68849b9..f34ab8f 100644 --- a/src/OpenApiType.php +++ b/src/OpenApiType.php @@ -282,10 +282,31 @@ enum: $values, return $type; } + if ($isIntersection) { + return new OpenApiType( + nullable: $nullable, + allOf: $items, + ); + } + + $itemTypes = array_map(static function (OpenApiType $item) { + if ($item->type !== null && $item->format !== null) { + return $item->type . '-' . $item->format; + } + + return $item->type; + }, $items); + + if (!empty(array_filter($itemTypes, static fn (?string $type) => $type === null)) || count($itemTypes) !== count(array_unique($itemTypes))) { + return new OpenApiType( + nullable: $nullable, + anyOf: $items, + ); + } + return new OpenApiType( nullable: $nullable, - oneOf: $isUnion ? $items : null, - allOf: $isIntersection ? $items : null, + oneOf: $items, ); } diff --git a/tests/appinfo/routes.php b/tests/appinfo/routes.php index 794154b..6fa6daf 100644 --- a/tests/appinfo/routes.php +++ b/tests/appinfo/routes.php @@ -69,5 +69,7 @@ ['name' => 'Settings#empty304', 'url' => '/api/{apiVersion}/304', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#passwordConfirmationAnnotation', 'url' => '/api/{apiVersion}/passwordConfirmationAnnotation', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#passwordConfirmationAttribute', 'url' => '/api/{apiVersion}/passwordConfirmationAttribute', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#oneOf', 'url' => '/api/{apiVersion}/oneOf', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#anyOf', 'url' => '/api/{apiVersion}/anyOf', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ], ]; diff --git a/tests/lib/Controller/SettingsController.php b/tests/lib/Controller/SettingsController.php index 4ea8dbd..de8ba72 100644 --- a/tests/lib/Controller/SettingsController.php +++ b/tests/lib/Controller/SettingsController.php @@ -486,4 +486,28 @@ public function passwordConfirmationAnnotation(): DataResponse { public function passwordConfirmationAttribute(): DataResponse { return new DataResponse(); } + + /** + * Route with oneOf + * + * @return DataResponse + * + * 200: OK + */ + #[PasswordConfirmationRequired] + public function oneOf(): DataResponse { + return new DataResponse(); + } + + /** + * Route with oneOf + * + * @return DataResponse + * + * 200: OK + */ + #[PasswordConfirmationRequired] + public function anyOf(): DataResponse { + return new DataResponse(); + } } diff --git a/tests/openapi-administration.json b/tests/openapi-administration.json index fa5737a..cb30b6a 100644 --- a/tests/openapi-administration.json +++ b/tests/openapi-administration.json @@ -2573,6 +2573,201 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/oneOf": { + "post": { + "operationId": "settings-one-of", + "summary": "Route with oneOf", + "description": "This endpoint requires admin access\nThis endpoint requires password confirmation", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "format": "int64" + }, + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "double" + }, + { + "type": "boolean" + } + ] + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/anyOf": { + "post": { + "operationId": "settings-any-of", + "summary": "Route with oneOf", + "description": "This endpoint requires admin access\nThis endpoint requires password confirmation", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "anyOf": [ + { + "type": "object", + "required": [ + "test" + ], + "properties": { + "test": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "test", + "abc" + ], + "properties": { + "test": { + "type": "string" + }, + "abc": { + "type": "integer", + "format": "int64" + } + } + } + ] + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/tests/attribute-ocs/{param}": { "get": { "operationId": "routing-attributeocs-route", diff --git a/tests/openapi-full.json b/tests/openapi-full.json index 93e0252..23c04fd 100644 --- a/tests/openapi-full.json +++ b/tests/openapi-full.json @@ -2700,6 +2700,201 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/oneOf": { + "post": { + "operationId": "settings-one-of", + "summary": "Route with oneOf", + "description": "This endpoint requires admin access\nThis endpoint requires password confirmation", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer", + "format": "int64" + }, + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "double" + }, + { + "type": "boolean" + } + ] + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/anyOf": { + "post": { + "operationId": "settings-any-of", + "summary": "Route with oneOf", + "description": "This endpoint requires admin access\nThis endpoint requires password confirmation", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v2" + ], + "default": "v2" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "anyOf": [ + { + "type": "object", + "required": [ + "test" + ], + "properties": { + "test": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "test", + "abc" + ], + "properties": { + "test": { + "type": "string" + }, + "abc": { + "type": "integer", + "format": "int64" + } + } + } + ] + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/tests/attribute-ocs/{param}": { "get": { "operationId": "routing-attributeocs-route",