diff --git a/generate-spec b/generate-spec index 163c926..5ebd5d1 100755 --- a/generate-spec +++ b/generate-spec @@ -154,7 +154,7 @@ if (file_exists($definitionsPath)) { } } foreach (array_keys($definitions) as $name) { - $schemas[Helpers::cleanSchemaName($name)] = OpenApiType::resolve('Response definitions', $definitions, $definitions[$name])->toArray(); + $schemas[Helpers::cleanSchemaName($name)] = OpenApiType::resolve('Response definitions: ' . $name, $definitions, $definitions[$name])->toArray(); } } else { Logger::debug('Response definitions', 'No response definitions were loaded'); diff --git a/src/OpenApiType.php b/src/OpenApiType.php index 8d0224a..1d335cb 100644 --- a/src/OpenApiType.php +++ b/src/OpenApiType.php @@ -174,6 +174,8 @@ public static function resolve(string $context, array $definitions, ParamTagValu } if ($node instanceof ArrayTypeNode) { + Logger::error($context, "The 'TYPE[]' syntax for arrays is forbidden due to ambiguities. Use 'list' for JSON arrays or 'array' for JSON objects instead."); + return new OpenApiType( context: $context, type: 'array', @@ -181,6 +183,10 @@ public static function resolve(string $context, array $definitions, ParamTagValu ); } if ($node instanceof GenericTypeNode && ($node->type->name === 'array' || $node->type->name === 'list' || $node->type->name === 'non-empty-list') && count($node->genericTypes) === 1) { + if ($node->type->name === 'array') { + Logger::error($context, "The 'array' syntax for arrays is forbidden due to ambiguities. Use 'list' for JSON arrays or 'array' for JSON objects instead."); + } + if ($node->genericTypes[0] instanceof IdentifierTypeNode && $node->genericTypes[0]->name === 'empty') { return new OpenApiType( context: $context, @@ -203,8 +209,8 @@ public static function resolve(string $context, array $definitions, ParamTagValu $properties = []; $required = []; foreach ($node->items as $item) { - $type = self::resolve($context, $definitions, $item->valueType); $name = $item->keyName instanceof ConstExprStringNode ? $item->keyName->value : $item->keyName->name; + $type = self::resolve($context . ': ' . $name, $definitions, $item->valueType); $properties[$name] = $type; if (!$item->optional) { $required[] = $name; diff --git a/tests/lib/Controller/AdminSettingsController.php b/tests/lib/Controller/AdminSettingsController.php index f4529ef..8af8ea7 100644 --- a/tests/lib/Controller/AdminSettingsController.php +++ b/tests/lib/Controller/AdminSettingsController.php @@ -18,7 +18,7 @@ class AdminSettingsController extends OCSController { /** * Route is only in the admin scope because there is no "NoAdminRequired" annotation or attribute * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ @@ -29,7 +29,7 @@ public function adminScopeImplicitFromAdminRequired(): DataResponse { /** * Route is in the default scope because the method overwrites with the Attribute * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ @@ -41,7 +41,7 @@ public function movedToDefaultScope(): DataResponse { /** * Route in default scope with tags * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ @@ -53,7 +53,7 @@ public function movedToSettingsTag(): DataResponse { /** * Route in default scope with tags but without named parameters on the attribute * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ diff --git a/tests/lib/Controller/ExAppSettingsController.php b/tests/lib/Controller/ExAppSettingsController.php index cc17de1..e0ff7c1 100644 --- a/tests/lib/Controller/ExAppSettingsController.php +++ b/tests/lib/Controller/ExAppSettingsController.php @@ -20,7 +20,7 @@ class ExAppSettingsController extends OCSController { /** * Route is in ex_app scope because of the attribute * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ @@ -32,7 +32,7 @@ public function exAppScopeAttribute(): DataResponse { /** * Route is in ex_app scope because of the override * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ diff --git a/tests/lib/Controller/FederationController.php b/tests/lib/Controller/FederationController.php index 57ba7e7..aacc605 100644 --- a/tests/lib/Controller/FederationController.php +++ b/tests/lib/Controller/FederationController.php @@ -29,7 +29,7 @@ class FederationController extends OCSController { * * Route is in federation scope as per controller scope * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: OK */ @@ -43,7 +43,7 @@ public function federationByController(): DataResponse { * Route is only in the default scope (moved from federation) * * @param NotificationsRequestProperty $property Property - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Personal settings updated */ diff --git a/tests/lib/Controller/RoutingController.php b/tests/lib/Controller/RoutingController.php index 338247e..ca5c929 100644 --- a/tests/lib/Controller/RoutingController.php +++ b/tests/lib/Controller/RoutingController.php @@ -16,7 +16,7 @@ class RoutingController extends OCSController { /** * OCS Route with attribute - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Success */ @@ -30,7 +30,7 @@ public function attributeOCS() { * @NoCSRFRequired * * Index Route with attribute - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Success */ diff --git a/tests/lib/Controller/SettingsController.php b/tests/lib/Controller/SettingsController.php index 1123589..6b76d78 100644 --- a/tests/lib/Controller/SettingsController.php +++ b/tests/lib/Controller/SettingsController.php @@ -29,7 +29,7 @@ class SettingsController extends OCSController { * * Route is ignored because of IgnoreOpenAPI attribute on the method * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: OK */ @@ -43,7 +43,7 @@ public function ignoreByDeprecatedAttributeOnMethod(): DataResponse { * * Route is ignored because of scope on the method * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: OK */ @@ -57,7 +57,7 @@ public function ignoreByScopeOnMethod(): DataResponse { * * Route is ignored because of scope on the method but without `scope: ` name * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: OK */ @@ -96,7 +96,7 @@ protected function createNotificationsPushDevice(): array { * * Route is in admin and default scope * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -136,7 +136,7 @@ public function listSchemas(): DataResponse { * A route with a limited set of possible integers * * @param 1|2|3|4|5|6|7|8|9|10 $limit Maximum number of objects - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -148,7 +148,7 @@ public function listOfIntParameters(int $limit): DataResponse { * A route with a min and max integers * * @param int<5, 10> $limit Between 5 and 10 - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -160,7 +160,7 @@ public function intParameterWithMinAndMax(int $limit): DataResponse { * A route with a min integers * * @param int<5, max> $limit At least 5 - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -172,7 +172,7 @@ public function intParameterWithMin(int $limit): DataResponse { * A route with a max integers * * @param int $limit At most 10 - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -184,7 +184,7 @@ public function intParameterWithMax(int $limit): DataResponse { * A route with a non negative integer * * @param non-negative-int $limit not negative - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -196,7 +196,7 @@ public function intParameterNonNegative(int $limit): DataResponse { * A route with a positive integer * * @param positive-int $limit positive - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -208,7 +208,7 @@ public function intParameterPositive(int $limit): DataResponse { * A route with a negative integer * * @param negative-int $limit negative - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -220,7 +220,7 @@ public function intParameterNegative(int $limit): DataResponse { * A route with a non positive integer * * @param non-positive-int $limit non positive - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -232,7 +232,7 @@ public function intParameterNonPositive(int $limit): DataResponse { * A route with a list of 2 integers, 2 strings and 1 boolean * * @param 0|1|'yes'|'no'|true $weird Weird list - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -244,7 +244,7 @@ public function listOfIntStringAndOneBool($weird): DataResponse { * A route with a list of 2 integers, 2 strings and 1 boolean * * @param 0|1|'yes'|'no'|true|false $weird Weird list - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -256,7 +256,7 @@ public function listOfIntStringAndAllBools($weird): DataResponse { * A route with required boolean * * @param bool $yesOrNo Boolean required - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -268,7 +268,7 @@ public function booleanParameterRequired(bool $yesOrNo): DataResponse { * A route with boolean defaulting to false * * @param bool $yesOrNo Boolean defaulting to false - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -280,7 +280,7 @@ public function booleanParameterDefaultFalse(bool $yesOrNo = false): DataRespons * A route with boolean defaulting to true * * @param bool $yesOrNo Boolean defaulting to true - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -292,7 +292,7 @@ public function booleanParameterDefaultTrue(bool $yesOrNo = true): DataResponse * A route with boolean or true * * @param bool|true $yesOrNo boolean or true - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -304,7 +304,7 @@ public function booleanTrueParameter(bool $yesOrNo): DataResponse { * A route with boolean or false * * @param bool|false $yesOrNo boolean or false - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -316,7 +316,7 @@ public function booleanFalseParameter(bool $yesOrNo): DataResponse { * A route with boolean or true or false * * @param bool|true|false $yesOrNo boolean or true or false - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -328,7 +328,7 @@ public function booleanTrueFalseParameter(bool $yesOrNo): DataResponse { * A route with true or false * * @param true|false $yesOrNo true or false - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -340,7 +340,7 @@ public function trueFalseParameter(bool $yesOrNo): DataResponse { * A route with string or 'test' * * @param string|'test' $value string or 'test' - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -352,7 +352,7 @@ public function stringValueParameter(string $value): DataResponse { * A route with int or 0 * * @param int|0 $value int or 0 - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -364,7 +364,7 @@ public function intValueParameter(int $value): DataResponse { * A route with numeric * * @param numeric $value Some numeric value - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -376,7 +376,7 @@ public function numericParameter(mixed $value): DataResponse { * A route with list * * @param list $value Some array value - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -388,7 +388,7 @@ public function arrayListParameter(array $value = ['test']): DataResponse { * A route with keyed array * * @param array $value Some array value - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: Admin settings updated */ @@ -401,7 +401,7 @@ public function arrayKeyedParameter(array $value = ['test' => 'abc']): DataRespo * * Route throws an OCS exception * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * @throws OCSNotFoundException Description of 404 because we throw all the time * * 200: Admin settings updated @@ -415,7 +415,7 @@ public function throwingOCS(): DataResponse { * * Route throws an OCS exception * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * @throws NotFoundException Description of 404 because we throw all the time * * 200: Admin settings updated @@ -427,7 +427,7 @@ public function throwingOther(): DataResponse { /** * A route 204 response * - * @return DataResponse, array{X-Custom: string}> + * @return DataResponse, array{X-Custom: string}> * * 204: No settings */ @@ -438,7 +438,7 @@ public function empty204(): DataResponse { /** * A route 304 response * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 304: No settings */ @@ -449,7 +449,7 @@ public function empty304(): DataResponse { /** * Route with password confirmation annotation * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * @PasswordConfirmationRequired * * 200: OK @@ -461,7 +461,7 @@ public function passwordConfirmationAnnotation(): DataResponse { /** * Route with password confirmation attribute * - * @return DataResponse, array{}> + * @return DataResponse, array{}> * * 200: OK */ @@ -513,7 +513,7 @@ public function floatDouble(): DataResponse { /** * Route with empty array * - * @return DataResponse}, array{}> + * @return DataResponse}, array{}> * * 200: OK */ @@ -527,7 +527,7 @@ public function emptyArray(): DataResponse { * * @param int $simple Value * @param array $complex Values - * @return DataResponse}, array{}> + * @return DataResponse}, array{}> * * 200: OK */ @@ -542,7 +542,7 @@ public function parameterRequestBody(int $simple, array $complex): DataResponse * * @param array $empty Empty * @param array $values Values - * @return DataResponse}, array{}> + * @return DataResponse}, array{}> * * 200: OK */ @@ -563,7 +563,7 @@ public function objectDefaults(array $empty = [], array $values = ['key' => 'val * has * even * more whitespace - * @return DataResponse}, array{}> + * @return DataResponse}, array{}> * * 200: OK */ diff --git a/tests/lib/ResponseDefinitions.php b/tests/lib/ResponseDefinitions.php index a99ee94..cad6d92 100644 --- a/tests/lib/ResponseDefinitions.php +++ b/tests/lib/ResponseDefinitions.php @@ -36,7 +36,7 @@ * subject: string, * message: string, * link: string, - * actions: NotificationsNotificationAction[], + * actions: list, * subjectRich?: string, * subjectRichParameters?: array, * messageRich?: string,