From 71bec68373a73729269f05474e88d791d6bfeba1 Mon Sep 17 00:00:00 2001 From: provokateurin Date: Mon, 27 Jan 2025 14:34:14 +0100 Subject: [PATCH] feat(OpenApiType): Suggest creating aliases for enums Signed-off-by: provokateurin --- generate-spec.php | 2 +- src/OpenApiType.php | 13 +- tests/appinfo/routes.php | 2 + tests/lib/Controller/SettingsController.php | 30 +++ tests/lib/ResponseDefinitions.php | 4 + tests/openapi-administration.json | 216 ++++++++++++++++++++ tests/openapi-full.json | 216 ++++++++++++++++++++ 7 files changed, 480 insertions(+), 3 deletions(-) diff --git a/generate-spec.php b/generate-spec.php index 8343358..55518cb 100755 --- a/generate-spec.php +++ b/generate-spec.php @@ -150,7 +150,7 @@ } } foreach (array_keys($definitions) as $name) { - $schemas[Helpers::cleanSchemaName($name)] = OpenApiType::resolve('Response definitions: ' . $name, $definitions, $definitions[$name])->toArray(); + $schemas[Helpers::cleanSchemaName($name)] = OpenApiType::resolve('Response definitions: ' . $name, $definitions, $definitions[$name], true)->toArray(); } } else { Logger::debug('Response definitions', 'No response definitions were loaded'); diff --git a/src/OpenApiType.php b/src/OpenApiType.php index b1a3ff8..96bc2e1 100644 --- a/src/OpenApiType.php +++ b/src/OpenApiType.php @@ -156,7 +156,7 @@ enum: [0, 1], return $values !== [] ? $values : new stdClass(); } - public static function resolve(string $context, array $definitions, ParamTagValueNode|NodeAbstract|TypeNode $node): OpenApiType { + public static function resolve(string $context, array $definitions, ParamTagValueNode|NodeAbstract|TypeNode $node, ?bool $isResponseDefinition = false): OpenApiType { if ($node instanceof ParamTagValueNode) { $type = self::resolve($context, $definitions, $node->type); $type->description = $node->description; @@ -276,6 +276,10 @@ public static function resolve(string $context, array $definitions, ParamTagValu ); } + if (!$isResponseDefinition) { + Logger::warning($context, 'Consider using a Response definition for this enum to improve readability and reusability.'); + } + return new OpenApiType( context: $context, type: 'string', @@ -298,6 +302,10 @@ enum: $values, ); } + if (!$isResponseDefinition) { + Logger::warning($context, 'Consider using a Response definition for this enum to improve readability and reusability.'); + } + return new OpenApiType( context: $context, type: 'integer', @@ -420,7 +428,8 @@ private static function mergeEnums(string $context, array $types): array { return array_merge($nonEnums, array_map(static fn (string $type): \OpenAPIExtractor\OpenApiType => new OpenApiType( context: $context, - type: $type, enum: $enums[$type], + type: $type, + enum: $enums[$type], ), array_keys($enums))); } diff --git a/tests/appinfo/routes.php b/tests/appinfo/routes.php index 5bf7eb9..64dcd75 100644 --- a/tests/appinfo/routes.php +++ b/tests/appinfo/routes.php @@ -76,5 +76,7 @@ ['name' => 'Settings#whitespace', 'url' => '/api/{apiVersion}/whitespace', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#withCorsAnnotation', 'url' => '/api/{apiVersion}/cors/annotation', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ['name' => 'Settings#withCorsAttribute', 'url' => '/api/{apiVersion}/cors/attribute', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#enumNotAliased', 'url' => '/api/{apiVersion}/enum/not-aliased', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], + ['name' => 'Settings#enumAliased', 'url' => '/api/{apiVersion}/enum/aliased', 'verb' => 'POST', 'requirements' => ['apiVersion' => '(v2)']], ], ]; diff --git a/tests/lib/Controller/SettingsController.php b/tests/lib/Controller/SettingsController.php index 4634ac0..40f94e7 100644 --- a/tests/lib/Controller/SettingsController.php +++ b/tests/lib/Controller/SettingsController.php @@ -23,6 +23,8 @@ * @psalm-import-type NotificationsPushDevice from ResponseDefinitions * @psalm-import-type NotificationsNotification from ResponseDefinitions * @psalm-import-type NotificationsCollection from ResponseDefinitions + * @psalm-import-type NotificationsEnumString from ResponseDefinitions + * @psalm-import-type NotificationsEnumInt from ResponseDefinitions */ class SettingsController extends OCSController { /** @@ -596,4 +598,32 @@ public function withCorsAnnotation(): DataResponse { public function withCorsAttribute(): DataResponse { return new DataResponse(); } + + /** + * Not aliased enum + * + * @param 'a'|'b' $string A string enum without alias + * @param 0|1 $int An int enum without alias + * + * @return DataResponse, array{}> + * + * 200: OK + */ + public function enumNotAliased(string $string, int $int): DataResponse { + return new DataResponse(); + } + + /** + * Aliased enum + * + * @param NotificationsEnumString $string A string enum with alias + * @param NotificationsEnumInt $int An int enum with alias + * + * @return DataResponse, array{}> + * + * 200: OK + */ + public function enumAliased(string $string, int $int): DataResponse { + return new DataResponse(); + } } diff --git a/tests/lib/ResponseDefinitions.php b/tests/lib/ResponseDefinitions.php index cad6d92..0642f38 100644 --- a/tests/lib/ResponseDefinitions.php +++ b/tests/lib/ResponseDefinitions.php @@ -59,6 +59,10 @@ * publicKey: string, * signature: string, * } + * + * @psalm-type NotificationsEnumString = 'a'|'b' + * + * @psalm-type NotificationsEnumInt = 0|1 */ class ResponseDefinitions { } diff --git a/tests/openapi-administration.json b/tests/openapi-administration.json index 8cfc83d..bce9e5d 100644 --- a/tests/openapi-administration.json +++ b/tests/openapi-administration.json @@ -40,6 +40,21 @@ } } }, + "EnumInt": { + "type": "integer", + "format": "int64", + "enum": [ + 0, + 1 + ] + }, + "EnumString": { + "type": "string", + "enum": [ + "a", + "b" + ] + }, "OCSMeta": { "type": "object", "required": [ @@ -4638,6 +4653,207 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/enum/not-aliased": { + "post": { + "operationId": "settings-enum-not-aliased", + "summary": "Not aliased enum", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "string", + "int" + ], + "properties": { + "string": { + "type": "string", + "enum": [ + "a", + "b" + ], + "description": "A string enum without alias" + }, + "int": { + "type": "integer", + "format": "int64", + "enum": [ + 0, + 1 + ], + "description": "An int enum without alias" + } + } + } + } + } + }, + "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": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/enum/aliased": { + "post": { + "operationId": "settings-enum-aliased", + "summary": "Aliased enum", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "string", + "int" + ], + "properties": { + "string": { + "$ref": "#/components/schemas/EnumString", + "description": "A string enum with alias" + }, + "int": { + "$ref": "#/components/schemas/EnumInt", + "description": "An int enum with alias" + } + } + } + } + } + }, + "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": {} + } + } + } + } + } + } + } + } + } + }, "/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 09b560b..e88b100 100644 --- a/tests/openapi-full.json +++ b/tests/openapi-full.json @@ -46,6 +46,21 @@ "$ref": "#/components/schemas/Item" } }, + "EnumInt": { + "type": "integer", + "format": "int64", + "enum": [ + 0, + 1 + ] + }, + "EnumString": { + "type": "string", + "enum": [ + "a", + "b" + ] + }, "Item": { "type": "object", "required": [ @@ -4788,6 +4803,207 @@ } } }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/enum/not-aliased": { + "post": { + "operationId": "settings-enum-not-aliased", + "summary": "Not aliased enum", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "string", + "int" + ], + "properties": { + "string": { + "type": "string", + "enum": [ + "a", + "b" + ], + "description": "A string enum without alias" + }, + "int": { + "type": "integer", + "format": "int64", + "enum": [ + 0, + 1 + ], + "description": "An int enum without alias" + } + } + } + } + } + }, + "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": {} + } + } + } + } + } + } + } + } + } + }, + "/ocs/v2.php/apps/notifications/api/{apiVersion}/enum/aliased": { + "post": { + "operationId": "settings-enum-aliased", + "summary": "Aliased enum", + "description": "This endpoint requires admin access", + "tags": [ + "settings" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "string", + "int" + ], + "properties": { + "string": { + "$ref": "#/components/schemas/EnumString", + "description": "A string enum with alias" + }, + "int": { + "$ref": "#/components/schemas/EnumInt", + "description": "An int enum with alias" + } + } + } + } + } + }, + "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": {} + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/tests/attribute-ocs/{param}": { "get": { "operationId": "routing-attributeocs-route",