From ce0c021ea6e336b150a2ea4578b2e4dd4d95e900 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 11 Nov 2024 16:12:27 +0100 Subject: [PATCH] fix: Allow more specific object keys Signed-off-by: Joas Schilling --- src/OpenApiType.php | 5 +- tests/appinfo/routes.php | 5 + .../lib/Controller/ReturnArraysController.php | 60 ++++ tests/openapi-administration.json | 308 ++++++++++++++++++ tests/openapi-full.json | 308 ++++++++++++++++++ 5 files changed, 684 insertions(+), 2 deletions(-) create mode 100644 tests/lib/Controller/ReturnArraysController.php diff --git a/src/OpenApiType.php b/src/OpenApiType.php index 1d335cb..fbae1b3 100644 --- a/src/OpenApiType.php +++ b/src/OpenApiType.php @@ -225,7 +225,8 @@ public static function resolve(string $context, array $definitions, ParamTagValu } if ($node instanceof GenericTypeNode && $node->type->name === 'array' && count($node->genericTypes) === 2 && $node->genericTypes[0] instanceof IdentifierTypeNode) { - if ($node->genericTypes[0]->name === 'string') { + $allowedTypes = ['string', 'lowercase-string', 'non-empty-string', 'non-empty-lowercase-string']; + if (in_array($node->genericTypes[0]->name, $allowedTypes, true)) { return new OpenApiType( context: $context, type: 'object', @@ -233,7 +234,7 @@ public static function resolve(string $context, array $definitions, ParamTagValu ); } - Logger::panic($context, "JSON objects can only be indexed by 'string' but got '" . $node->genericTypes[0]->name . "'"); + Logger::panic($context, "JSON objects can only be indexed by '" . implode("', '", $allowedTypes) . "' but got '" . $node->genericTypes[0]->name . "'"); } if ($node instanceof GenericTypeNode && $node->type->name == 'int' && count($node->genericTypes) == 2) { diff --git a/tests/appinfo/routes.php b/tests/appinfo/routes.php index e9cacaf..25826c9 100644 --- a/tests/appinfo/routes.php +++ b/tests/appinfo/routes.php @@ -21,6 +21,11 @@ ['name' => 'Federation#federationByController', 'url' => '/api/{apiVersion}/controller-scope', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Federation#movedToDefaultScope', 'url' => '/api/{apiVersion}/default-scope', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'ReturnArrays#stringArray', 'url' => '/api/{apiVersion}/return-arrays/string', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'ReturnArrays#nonEmptyStringArray', 'url' => '/api/{apiVersion}/return-arrays/non-empty-string', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'ReturnArrays#lowercaseStringArray', 'url' => '/api/{apiVersion}/return-arrays/lowercase-string', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'ReturnArrays#nonEmptyLowercaseStringArray', 'url' => '/api/{apiVersion}/return-arrays/non-empty-lowercase-string', 'verb' => 'GET', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#ignoreByDeprecatedAttributeOnMethod', 'url' => '/api/{apiVersion}/ignore-openapi-attribute', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#ignoreByScopeOnMethod', 'url' => '/api/{apiVersion}/ignore-method-scope', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#ignoreByUnnamedScopeOnMethod', 'url' => '/api/{apiVersion}/ignore-method-scope-unnamed', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], diff --git a/tests/lib/Controller/ReturnArraysController.php b/tests/lib/Controller/ReturnArraysController.php new file mode 100644 index 0000000..749ff0b --- /dev/null +++ b/tests/lib/Controller/ReturnArraysController.php @@ -0,0 +1,60 @@ +, array{}> + * + * 200: OK + */ + public function stringArray(): DataResponse { + return new DataResponse(); + } + + /** + * Route with array using non-empty-string keys + * + * @return DataResponse, array{}> + * + * 200: OK + */ + public function nonEmptyStringArray(): DataResponse { + return new DataResponse(); + } + + /** + * Route with array using lowercase-string keys + * + * @return DataResponse, array{}> + * + * 200: OK + */ + public function lowercaseStringArray(): DataResponse { + return new DataResponse(); + } + + /** + * Route with array using non-empty-lowercase-string keys + * + * @return DataResponse, array{}> + * + * 200: OK + */ + public function nonEmptyLowercaseStringArray(): DataResponse { + return new DataResponse(); + } +} diff --git a/tests/openapi-administration.json b/tests/openapi-administration.json index 6e30b23..737a42d 100644 --- a/tests/openapi-administration.json +++ b/tests/openapi-administration.json @@ -298,6 +298,314 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/string": { + "get": { + "operationId": "return_arrays-string-array", + "summary": "Route with array using string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/non-empty-string": { + "get": { + "operationId": "return_arrays-non-empty-string-array", + "summary": "Route with array using non-empty-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/lowercase-string": { + "get": { + "operationId": "return_arrays-lowercase-string-array", + "summary": "Route with array using lowercase-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/non-empty-lowercase-string": { + "get": { + "operationId": "return_arrays-non-empty-lowercase-string-array", + "summary": "Route with array using non-empty-lowercase-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/notifications/api/{apiVersion}/admin-scope": { "post": { "operationId": "settings-moved-to-admin-scope", diff --git a/tests/openapi-full.json b/tests/openapi-full.json index 4330e57..a5dbf1b 100644 --- a/tests/openapi-full.json +++ b/tests/openapi-full.json @@ -448,6 +448,314 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/string": { + "get": { + "operationId": "return_arrays-string-array", + "summary": "Route with array using string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/non-empty-string": { + "get": { + "operationId": "return_arrays-non-empty-string-array", + "summary": "Route with array using non-empty-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/lowercase-string": { + "get": { + "operationId": "return_arrays-lowercase-string-array", + "summary": "Route with array using lowercase-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/return-arrays/non-empty-lowercase-string": { + "get": { + "operationId": "return_arrays-non-empty-lowercase-string-array", + "summary": "Route with array using non-empty-lowercase-string keys", + "description": "This endpoint requires admin access", + "tags": [ + "return_arrays" + ], + "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": { + "type": "object", + "additionalProperties": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/notifications/api/{apiVersion}/admin-scope": { "post": { "operationId": "settings-moved-to-admin-scope",