From 0af05504ca7182006b7a6704deeb0918c2723b4b Mon Sep 17 00:00:00 2001 From: Curtis Conard Date: Tue, 29 Oct 2024 18:45:52 -0400 Subject: [PATCH 1/2] fix openapi schema validation issues --- .../Api/HL/Controller/AssetController.php | 17 ++++++- src/Glpi/Api/HL/Controller/CoreController.php | 49 +++++++------------ src/Glpi/Api/HL/Controller/ITILController.php | 4 +- .../HL/Controller/ManagementController.php | 10 ---- src/Glpi/Api/HL/OpenAPIGenerator.php | 21 ++------ src/Glpi/Api/HL/RoutePath.php | 9 ++++ 6 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/Glpi/Api/HL/Controller/AssetController.php b/src/Glpi/Api/HL/Controller/AssetController.php index bfda0348f46..9f1a9a0a078 100644 --- a/src/Glpi/Api/HL/Controller/AssetController.php +++ b/src/Glpi/Api/HL/Controller/AssetController.php @@ -1680,6 +1680,7 @@ public function deleteCartridgeItems(Request $request): Response } #[Route(path: '/Cartridge/{cartridgeitems_id}/{id}', methods: ['GET'], requirements: [ + 'cartridgeitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'], middlewares: [ResultFormatterMiddleware::class])] #[RouteVersion(introduced: '2.0')] @@ -1694,7 +1695,9 @@ public function getCartridge(Request $request): Response return Search::getOneBySchema($this->getKnownSchema('Cartridge', $this->getAPIVersion($request)), $request->getAttributes(), $request->getParameters()); } - #[Route(path: '/Cartridge/{cartridgeitems_id}', methods: ['POST'], tags: ['Assets'])] + #[Route(path: '/Cartridge/{cartridgeitems_id}', methods: ['POST'], tags: ['Assets'], requirements: [ + 'cartridgeitems_id' => '\d+' + ])] #[RouteVersion(introduced: '2.0')] #[Doc\Route( description: 'Create a cartridge', @@ -1712,6 +1715,7 @@ public function createCartridges(Request $request): Response } #[Route(path: '/Cartridge/{cartridgeitems_id}/{id}', methods: ['PATCH'], requirements: [ + 'cartridgeitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] @@ -1731,6 +1735,7 @@ public function updateCartridges(Request $request): Response } #[Route(path: '/Cartridge/{cartridgeitems_id}/{id}', methods: ['DELETE'], requirements: [ + 'cartridgeitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] @@ -1820,6 +1825,7 @@ public function deleteConsumableItems(Request $request): Response } #[Route(path: '/Consumable/{consumableitems_id}/{id}', methods: ['GET'], requirements: [ + 'consumableitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'], middlewares: [ResultFormatterMiddleware::class])] #[RouteVersion(introduced: '2.0')] @@ -1834,7 +1840,9 @@ public function getConsumable(Request $request): Response return Search::getOneBySchema($this->getKnownSchema('Consumable', $this->getAPIVersion($request)), $request->getAttributes(), $request->getParameters()); } - #[Route(path: '/Consumable/{consumableitems_id}', methods: ['POST'], tags: ['Assets'])] + #[Route(path: '/Consumable/{consumableitems_id}', methods: ['POST'], requirements: [ + 'consumableitems_id' => '\d+' + ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] #[Doc\Route( description: 'Create a consumable', @@ -1852,6 +1860,7 @@ public function createConsumables(Request $request): Response } #[Route(path: '/Consumable/{consumableitems_id}/{id}', methods: ['PATCH'], requirements: [ + 'consumableitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] @@ -1871,6 +1880,7 @@ public function updateConsumable(Request $request): Response } #[Route(path: '/Consumable/{consumableitems_id}/{id}', methods: ['DELETE'], requirements: [ + 'consumableitems_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] @@ -2546,6 +2556,7 @@ public function searchSoftwareVersions(Request $request): Response } #[Route(path: '/Software/{software_id}/Version/{id}', methods: ['GET'], requirements: [ + 'software_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'], middlewares: [ResultFormatterMiddleware::class])] #[RouteVersion(introduced: '2.0')] @@ -2590,6 +2601,7 @@ public function createSoftwareVersion(Request $request): Response } #[Route(path: '/Software/{software_id}/Version/{id}', methods: ['PATCH'], requirements: [ + 'software_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] @@ -2609,6 +2621,7 @@ public function updateSoftwareVersion(Request $request): Response } #[Route(path: '/Software/{software_id}/Version/{id}', methods: ['DELETE'], requirements: [ + 'software_id' => '\d+', 'id' => '\d+' ], tags: ['Assets'])] #[RouteVersion(introduced: '2.0')] diff --git a/src/Glpi/Api/HL/Controller/CoreController.php b/src/Glpi/Api/HL/Controller/CoreController.php index a174dedab4d..10dff09aeea 100644 --- a/src/Glpi/Api/HL/Controller/CoreController.php +++ b/src/Glpi/Api/HL/Controller/CoreController.php @@ -100,6 +100,24 @@ public static function getRawKnownSchemas(): array 'entity' => ['type' => Doc\Schema::TYPE_INTEGER], 'options' => ['type' => Doc\Schema::TYPE_OBJECT], ] + ], + 'APIInformation' => [ + 'x-version-introduced' => '2.0', + 'type' => Doc\Schema::TYPE_OBJECT, + 'properties' => [ + 'message' => ['type' => Doc\Schema::TYPE_STRING], + 'api_versions' => [ + 'type' => Doc\Schema::TYPE_ARRAY, + 'items' => [ + 'type' => Doc\Schema::TYPE_OBJECT, + 'properties' => [ + 'api_version' => ['type' => Doc\Schema::TYPE_STRING], + 'version' => ['type' => Doc\Schema::TYPE_STRING], + 'endpoint' => ['type' => Doc\Schema::TYPE_STRING], + ] + ] + ] + ] ] ]; } @@ -111,34 +129,7 @@ public static function getRawKnownSchemas(): array responses: [ '200' => [ 'description' => 'API information', - 'schema' => [ - 'type' => Doc\Schema::TYPE_OBJECT, - 'properties' => [ - 'message' => ['type' => Doc\Schema::TYPE_STRING], - 'api_versions' => [ - 'type' => Doc\Schema::TYPE_ARRAY, - 'items' => [ - 'type' => Doc\Schema::TYPE_OBJECT, - 'properties' => [ - 'api_version' => ['type' => Doc\Schema::TYPE_STRING], - 'version' => ['type' => Doc\Schema::TYPE_STRING], - 'endpoint' => ['type' => Doc\Schema::TYPE_STRING], - ] - ] - ], - 'links' => [ - 'type' => Doc\Schema::TYPE_ARRAY, - 'items' => [ - 'type' => Doc\Schema::TYPE_OBJECT, - 'properties' => [ - 'href' => ['type' => Doc\Schema::TYPE_STRING], - 'methods' => ['type' => Doc\Schema::TYPE_ARRAY, 'items' => ['type' => Doc\Schema::TYPE_STRING]], - 'requirements' => ['type' => Doc\Schema::TYPE_OBJECT, 'properties' => []], - ] - ] - ] - ] - ] + 'schema' => 'APIInformation' ] ] )] @@ -149,8 +140,6 @@ public function index(Request $request): Response 'api_versions' => Router::getAPIVersions() ]; - $data['links'] = Router::getInstance()->getAllRoutePaths(); - return new JSONResponse($data); } diff --git a/src/Glpi/Api/HL/Controller/ITILController.php b/src/Glpi/Api/HL/Controller/ITILController.php index 5e5d71a6e44..391f3be99d6 100644 --- a/src/Glpi/Api/HL/Controller/ITILController.php +++ b/src/Glpi/Api/HL/Controller/ITILController.php @@ -211,7 +211,6 @@ public static function getRawKnownSchemas(): array $schemas[$itil_type]['properties']['team'] = [ 'type' => Doc\Schema::TYPE_ARRAY, - 'x-full-schema' => 'TeamMember', 'items' => [ 'x-mapped-from' => 'id', 'x-mapper' => function ($v) use ($itil_type) { @@ -221,7 +220,8 @@ public static function getRawKnownSchemas(): array } return []; }, - 'ref' => 'TeamMember' + 'type' => Doc\Schema::TYPE_OBJECT, + 'x-full-schema' => 'TeamMember', ] ]; } diff --git a/src/Glpi/Api/HL/Controller/ManagementController.php b/src/Glpi/Api/HL/Controller/ManagementController.php index d694f496abc..60dfbfa3531 100644 --- a/src/Glpi/Api/HL/Controller/ManagementController.php +++ b/src/Glpi/Api/HL/Controller/ManagementController.php @@ -81,21 +81,11 @@ public static function getManagementTypes(bool $schema_names_only = true): array if ($management_types === null) { $management_types = [ - Appliance::class => [ - 'schema_name' => 'Appliance', - 'label' => Appliance::getTypeName(1), - 'version_introduced' => '2.0' - ], Budget::class => [ 'schema_name' => 'Budget', 'label' => Budget::getTypeName(1), 'version_introduced' => '2.0' ], - Certificate::class => [ - 'schema_name' => 'Certificate', - 'label' => Certificate::getTypeName(1), - 'version_introduced' => '2.0' - ], Cluster::class => [ 'schema_name' => 'Cluster', 'label' => Cluster::getTypeName(1), diff --git a/src/Glpi/Api/HL/OpenAPIGenerator.php b/src/Glpi/Api/HL/OpenAPIGenerator.php index 2d693f4711f..3e97acdfdfc 100644 --- a/src/Glpi/Api/HL/OpenAPIGenerator.php +++ b/src/Glpi/Api/HL/OpenAPIGenerator.php @@ -39,6 +39,7 @@ use Glpi\Api\HL\Doc\Response; use Glpi\Api\HL\Doc\Schema; use Glpi\Api\HL\Doc\SchemaReference; +use Glpi\Api\HL\Middleware\ResultFormatterMiddleware; use Glpi\OAuth\Server; /** @@ -556,21 +557,11 @@ private function getPathParameterSchema(Doc\Parameter $route_param): array */ private function getPathSecuritySchema(RoutePath $route_path, string $route_method): array { - $schemas = [ + return [ [ 'oauth' => [] ] ]; - // Handle special Session case - if ($route_path->getRouteSecurityLevel() !== Route::SECURITY_NONE) { - $schemas = array_merge($schemas, [ - [ - 'sessionTokenAuth' => [] - ] - ]); - } - - return $schemas; } private function getPathResponseSchemas(RoutePath $route_path, string $method): array @@ -596,7 +587,7 @@ private function getPathResponseSchemas(RoutePath $route_path, string $method): ] ], ]; - if ($response_media_type === 'application/json') { + if ($response_media_type === 'application/json' && $route_path->hasMiddleware(ResultFormatterMiddleware::class)) { // add csv and xml $response_schema['content']['text/csv'] = [ 'schema' => $resolved_schema @@ -696,8 +687,6 @@ private function getPathSchemas(RoutePath $route_path): array if (isset($info['content'])) { $path_schema['responses'][$code]['content'] = $info['content']; } - } else { - $path_schema['responses'][$code]['produces'] = array_keys($response_schema[(int) $code]['content']); } } @@ -733,7 +722,7 @@ private function getPathSchemas(RoutePath $route_path): array $param = [ 'name' => $name, 'in' => 'path', - 'required' => 'true', + 'required' => true, 'schema' => [ 'type' => 'integer', 'pattern' => $requirement @@ -743,7 +732,7 @@ private function getPathSchemas(RoutePath $route_path): array $param = [ 'name' => $name, 'in' => 'path', - 'required' => 'true', + 'required' => true, 'schema' => [ 'type' => 'string', 'pattern' => $requirement diff --git a/src/Glpi/Api/HL/RoutePath.php b/src/Glpi/Api/HL/RoutePath.php index a82ffff2e7c..718ff964c26 100644 --- a/src/Glpi/Api/HL/RoutePath.php +++ b/src/Glpi/Api/HL/RoutePath.php @@ -372,6 +372,15 @@ public function getMiddlewares(): array return $this->getRoute()->middlewares; } + /** + * Check if a middleware is present in the route + * @param class-string $middleware + */ + public function hasMiddleware(string $middleware): bool + { + return in_array($middleware, $this->getMiddlewares(), true); + } + /** * Combine data from the class Route attribute (if present) with the method's own attribute * From 022913b90cf71cf07c940a3ae86fb8da151ac698 Mon Sep 17 00:00:00 2001 From: Curtis Conard Date: Thu, 31 Oct 2024 06:56:44 -0400 Subject: [PATCH 2/2] fix some ci issues --- .phpstan-baseline.php | 90 ------------------- .../Api/HL/Controller/AbstractController.php | 4 +- src/Glpi/Api/HL/Controller/ITILController.php | 33 +++---- src/Glpi/Api/HL/OpenAPIGenerator.php | 7 +- src/Glpi/Api/HL/Route.php | 2 + src/Glpi/Api/HL/RoutePath.php | 12 +-- .../HL/Controller/ManagementController.php | 2 +- 7 files changed, 31 insertions(+), 119 deletions(-) diff --git a/.phpstan-baseline.php b/.phpstan-baseline.php index 28d342e9983..c515ebf29d9 100644 --- a/.phpstan-baseline.php +++ b/.phpstan-baseline.php @@ -1621,36 +1621,12 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/APIRest.php', ]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\AbstractController\\:\\:getKnownSchema\\(\\) should return array\\|null but returns Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\|null\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/AbstractController.php', -]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\AdministrationController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/AdministrationController.php', -]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ComponentController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ComponentController.php', -]; $ignoreErrors[] = [ // identifier: return.type 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\CoreController\\:\\:authorize\\(\\) should return Glpi\\\\Http\\\\Response but returns Psr\\\\Http\\\\Message\\\\ResponseInterface\\.$#', 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/CoreController.php', ]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\CoreController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\|string\\>\\>\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/CoreController.php', -]; $ignoreErrors[] = [ // identifier: return.type 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\CoreController\\:\\:token\\(\\) should return Glpi\\\\Http\\\\Response but returns Psr\\\\Http\\\\Message\\\\ResponseInterface\\.$#', @@ -1663,18 +1639,6 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/CoreController.php', ]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\DropdownController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/DropdownController.php', -]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ITILController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ITILController.php', -]; $ignoreErrors[] = [ // identifier: method.unused 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ITILController\\:\\:getSubitemLinkFields\\(\\) is unused\\.$#', @@ -1723,12 +1687,6 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ManagementController.php', ]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ManagementController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ManagementController.php', -]; $ignoreErrors[] = [ // identifier: offsetAccess.notFound 'message' => '#^Offset \'label\' does not exist on string\\.$#', @@ -1747,36 +1705,12 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ManagementController.php', ]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ProjectController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ProjectController.php', -]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\ReportController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ReportController.php', -]; -$ignoreErrors[] = [ - // identifier: return.type - 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Controller\\\\RuleController\\:\\:getRawKnownSchemas\\(\\) should return array\\ but returns array\\\\|string\\>\\>\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/RuleController.php', -]; $ignoreErrors[] = [ // identifier: nullCoalesce.offset 'message' => '#^Offset \'fields\' on string on left side of \\?\\? does not exist\\.$#', 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/RuleController.php', ]; -$ignoreErrors[] = [ - // identifier: function.impossibleType - 'message' => '#^Call to function array_key_exists\\(\\) with \'x\\-itemtype\' and Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema will always evaluate to false\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Doc/Schema.php', -]; $ignoreErrors[] = [ // identifier: return.type 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\:\\:getUnionSchema\\(\\) should return array\\{x\\-subtypes\\: array\\{schema_name\\: string, itemtype\\: string\\}, type\\: \'object\', properties\\: array\\} but returns array\\{x\\-subtypes\\: non\\-empty\\-array\\, array\\{schema_name\\: string, itemtype\\: mixed\\}\\>, type\\: \'object\', properties\\: mixed\\}\\.$#', @@ -1789,42 +1723,18 @@ 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Doc/Schema.php', ]; -$ignoreErrors[] = [ - // identifier: booleanAnd.alwaysFalse - 'message' => '#^Result of && is always false\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/Doc/Schema.php', -]; $ignoreErrors[] = [ // identifier: booleanAnd.rightAlwaysFalse 'message' => '#^Right side of && is always false\\.$#', 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/Middleware/DebugRequestMiddleware.php', ]; -$ignoreErrors[] = [ - // identifier: nullCoalesce.offset - 'message' => '#^Offset \'parameters\' on array\\{tags\\: array\\, responses\\: array, description\\?\\: string, parameters\\: non\\-empty\\-array\\, requestBody\\?\\: array\\{content\\: array\\{application/json\\: array\\{schema\\: array\\{type\\: string, format\\?\\: string, pattern\\?\\: string, properties\\?\\: array\\\\}\\}\\}\\}, security\\: array\\\\>\\>\\} on left side of \\?\\? always exists and is not nullable\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/OpenAPIGenerator.php', -]; $ignoreErrors[] = [ // identifier: smaller.alwaysFalse 'message' => '#^Comparison operation "\\<" between 0 and 0 is always false\\.$#', 'count' => 1, 'path' => __DIR__ . '/src/Glpi/Api/HL/RSQL/Lexer.php', ]; -$ignoreErrors[] = [ - // identifier: phpDoc.parseError - 'message' => '#^PHPDoc tag @phpstan\\-type RoutePathCacheHint has invalid value\\: Unexpected token "\\{", expected type at offset 40$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/RoutePath.php', -]; -$ignoreErrors[] = [ - // identifier: return.phpDocType - 'message' => '#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#', - 'count' => 1, - 'path' => __DIR__ . '/src/Glpi/Api/HL/RoutePath.php', -]; $ignoreErrors[] = [ // identifier: return.unusedType 'message' => '#^Method Glpi\\\\Api\\\\HL\\\\Router\\:\\:doRequestMiddleware\\(\\) never returns Glpi\\\\Http\\\\Response so it can be removed from the return type\\.$#', diff --git a/src/Glpi/Api/HL/Controller/AbstractController.php b/src/Glpi/Api/HL/Controller/AbstractController.php index 8b4f9f88801..d3f3cdf0354 100644 --- a/src/Glpi/Api/HL/Controller/AbstractController.php +++ b/src/Glpi/Api/HL/Controller/AbstractController.php @@ -127,7 +127,7 @@ protected function getAPIVersion(Request $request): string } /** - * @return array + * @return array */ protected static function getRawKnownSchemas(): array { @@ -138,7 +138,7 @@ protected static function getRawKnownSchemas(): array * Get all known schemas for this controller for the requested API version * @param ?string $api_version The API version or null if all versions should be returned * @return array - * @phpstan-return array + * @phpstan-return array */ final public static function getKnownSchemas(?string $api_version): array { diff --git a/src/Glpi/Api/HL/Controller/ITILController.php b/src/Glpi/Api/HL/Controller/ITILController.php index 391f3be99d6..5aae4dccaf3 100644 --- a/src/Glpi/Api/HL/Controller/ITILController.php +++ b/src/Glpi/Api/HL/Controller/ITILController.php @@ -168,6 +168,22 @@ public static function getRawKnownSchemas(): array ] ]; + $schemas['TeamMember'] = [ + 'x-version-introduced' => '2.0', + 'type' => Doc\Schema::TYPE_OBJECT, + 'description' => 'The valid types and roles depend on the type of the item they are being added to', + 'properties' => [ + 'id' => [ + 'type' => Doc\Schema::TYPE_INTEGER, + 'format' => Doc\Schema::FORMAT_INTEGER_INT64, + 'x-readonly' => true, + ], + 'name' => ['type' => Doc\Schema::TYPE_STRING], + 'type' => ['type' => Doc\Schema::TYPE_STRING], + 'role' => ['type' => Doc\Schema::TYPE_STRING], + ] + ]; + $itil_types = [Ticket::class, Change::class, Problem::class]; /** @var class-string $itil_type */ @@ -221,6 +237,7 @@ public static function getRawKnownSchemas(): array return []; }, 'type' => Doc\Schema::TYPE_OBJECT, + 'properties' => $schemas['TeamMember']['properties'], 'x-full-schema' => 'TeamMember', ] ]; @@ -425,22 +442,6 @@ public static function getRawKnownSchemas(): array $schemas['ChangeValidation']['x-itemtype'] = \ChangeValidation::class; $schemas['ChangeValidation']['properties'][Change::getForeignKeyField()] = ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT64]; - $schemas['TeamMember'] = [ - 'x-version-introduced' => '2.0', - 'type' => Doc\Schema::TYPE_OBJECT, - 'description' => 'The valid types and roles depend on the type of the item they are being added to', - 'properties' => [ - 'id' => [ - 'type' => Doc\Schema::TYPE_INTEGER, - 'format' => Doc\Schema::FORMAT_INTEGER_INT64, - 'x-readonly' => true, - ], - 'name' => ['type' => Doc\Schema::TYPE_STRING], - 'type' => ['type' => Doc\Schema::TYPE_STRING], - 'role' => ['type' => Doc\Schema::TYPE_STRING], - ] - ]; - $schemas['RecurringTicket'] = [ 'x-version-introduced' => '2.0', 'x-itemtype' => \TicketRecurrent::class, diff --git a/src/Glpi/Api/HL/OpenAPIGenerator.php b/src/Glpi/Api/HL/OpenAPIGenerator.php index 3e97acdfdfc..35c9e2fc10e 100644 --- a/src/Glpi/Api/HL/OpenAPIGenerator.php +++ b/src/Glpi/Api/HL/OpenAPIGenerator.php @@ -35,6 +35,7 @@ namespace Glpi\Api\HL; +use CommonGLPI; use Glpi\Api\HL\Controller\ProjectController; use Glpi\Api\HL\Doc\Response; use Glpi\Api\HL\Doc\Schema; @@ -164,6 +165,7 @@ public static function getComponentSchemas(string $api_version): array unset($schemas[$schema_name]); } if (!isset($known_schema['description']) && isset($known_schema['x-itemtype'])) { + /** @var class-string $itemtype */ $itemtype = $known_schema['x-itemtype']; $known_schema['description'] = method_exists($itemtype, 'getTypeName') ? $itemtype::getTypeName(1) : 'No description available'; } @@ -357,9 +359,6 @@ private function expandGenericPaths(array $paths): array { $expanded = []; foreach ($paths as $path_url => $path) { - if (str_contains($path_url, 'Timeline')) { - $t = ''; - } foreach ($path as $method => $route) { $new_urls = []; /** @var array $all_expansions All path expansions where the keys are the placeholder name and the values are arrays of possible replacements */ @@ -805,7 +804,7 @@ private function getPathSchemas(RoutePath $route_path): array $path_schema['requestBody'] = $request_body; } $path_schema['security'] = $this->getPathSecuritySchema($route_path, $route_method); - $path_schema['parameters'] = array_values($path_schema['parameters'] ?? []); + $path_schema['parameters'] = array_values($path_schema['parameters']); $path_schema['x-controller'] = $route_path->getController(); $path_schemas[$method] = $path_schema; } diff --git a/src/Glpi/Api/HL/Route.php b/src/Glpi/Api/HL/Route.php index 22bea37f178..4d3421eb3e6 100644 --- a/src/Glpi/Api/HL/Route.php +++ b/src/Glpi/Api/HL/Route.php @@ -36,6 +36,7 @@ namespace Glpi\Api\HL; use Attribute; +use Glpi\Api\HL\Middleware\AbstractMiddleware; #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] class Route @@ -62,6 +63,7 @@ public function __construct( public int $security_level = self::SECURITY_AUTHENTICATED, /** @var string[] */ public array $tags = [], + /** @var class-string[] */ public array $middlewares = [], ) { } diff --git a/src/Glpi/Api/HL/RoutePath.php b/src/Glpi/Api/HL/RoutePath.php index 718ff964c26..6a82e6e4a78 100644 --- a/src/Glpi/Api/HL/RoutePath.php +++ b/src/Glpi/Api/HL/RoutePath.php @@ -45,29 +45,29 @@ use ReflectionMethod; /** - * @phpstan-type RoutePathCacheHint {key: string, path: string, compiled_path: string, methods: string[], priority: int, security: int} + * @phpstan-type RoutePathCacheHint array{key: string, path: string, compiled_path: string, methods: string[], priority: int, security: int} */ final class RoutePath { /** * The Route attribute - * @var Route + * @var Route|null */ private ?Route $route = null; /** - * @var ReflectionClass + * @var ReflectionClass|null */ private ?ReflectionClass $controller = null; /** - * @var ReflectionMethod + * @var ReflectionMethod|null */ private ?ReflectionMethod $method = null; /** * The relative URI path with placeholder requirements inlined - * @var string + * @var string|null */ private ?string $compiled_path; @@ -365,7 +365,7 @@ private function setPriority(int $priority) } /** - * @return AbstractMiddleware[] + * @return class-string[] */ public function getMiddlewares(): array { diff --git a/tests/functional/Glpi/Api/HL/Controller/ManagementController.php b/tests/functional/Glpi/Api/HL/Controller/ManagementController.php index f5e593124ce..8ce32f95a17 100644 --- a/tests/functional/Glpi/Api/HL/Controller/ManagementController.php +++ b/tests/functional/Glpi/Api/HL/Controller/ManagementController.php @@ -42,7 +42,7 @@ class ManagementController extends \HLAPITestCase public function testCreateGetUpdateDelete() { $management_types = [ - 'Appliance', 'Budget', 'Certificate', 'Cluster', 'Contact', 'Contract', 'Database', + 'Budget', 'Cluster', 'Contact', 'Contract', 'Database', 'DataCenter', 'Document', 'Domain', 'Line', 'Supplier', ];