Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OpenAPI schema validation issues #18161

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 0 additions & 90 deletions .phpstan-baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, array\\|\\(Closure\\)\\>\\|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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, 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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, array\\<string, array\\<string, array\\<string, string\\>\\|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\\.$#',
Expand All @@ -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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, 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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<int\\|string, array\\|\\(Closure\\)\\>\\|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\\.$#',
Expand Down Expand Up @@ -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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<int\\|string, array\\<string, mixed\\>\\>\\.$#',
'count' => 1,
'path' => __DIR__ . '/src/Glpi/Api/HL/Controller/ManagementController.php',
];
$ignoreErrors[] = [
// identifier: offsetAccess.notFound
'message' => '#^Offset \'label\' does not exist on string\\.$#',
Expand All @@ -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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, array\\|\\(Closure\\)\\>\\|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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, array\\<string, array\\<string, array\\|string\\>\\|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\\<string, Glpi\\\\Api\\\\HL\\\\Doc\\\\Schema\\> but returns array\\<string, array\\<string, array\\<string, 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\\<int\\<0, max\\>, array\\{schema_name\\: string, itemtype\\: mixed\\}\\>, type\\: \'object\', properties\\: mixed\\}\\.$#',
Expand All @@ -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\\<string\\>, responses\\: array, description\\?\\: string, parameters\\: non\\-empty\\-array\\<string, array\\{name\\: \'Accept\\-Language\', in\\: \'header\', description\\: \'The language to use…\', schema\\: array\\{type\\: \'string\'\\}, examples\\: array\\{English_GB\\: array\\{value\\: \'en_GB\', summary\\: \'English \\(United…\'\\}, French_FR\\: array\\{value\\: \'fr_FR\', summary\\: \'French \\(France\\)\'\\}, Portuguese_BR\\: array\\{value\\: \'pt_BR\', summary\\: \'Portuguese \\(Brazil\\)\'\\}\\}\\}\\|array\\{name\\: string, in\\: string, description\\: string, required\\?\\: \'true\'\\|bool, schema\\?\\: mixed\\}\\>, requestBody\\?\\: array\\{content\\: array\\{application/json\\: array\\{schema\\: array\\{type\\: string, format\\?\\: string, pattern\\?\\: string, properties\\?\\: array\\<string, array\\{type\\: string, format\\?\\: string\\}\\>\\}\\}\\}\\}, security\\: array\\<array\\<string, array\\<string, mixed\\>\\>\\>\\} 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\\.$#',
Expand Down
4 changes: 2 additions & 2 deletions src/Glpi/Api/HL/Controller/AbstractController.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected function getAPIVersion(Request $request): string
}

/**
* @return array<string, Doc\Schema>
* @return array<string, array>
*/
protected static function getRawKnownSchemas(): array
{
Expand All @@ -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<string, Doc\Schema>
* @phpstan-return array<string, array>
*/
final public static function getKnownSchemas(?string $api_version): array
{
Expand Down
17 changes: 15 additions & 2 deletions src/Glpi/Api/HL/Controller/AssetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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')]
Expand All @@ -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',
Expand All @@ -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')]
Expand All @@ -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')]
Expand Down Expand Up @@ -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')]
Expand All @@ -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',
Expand All @@ -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')]
Expand All @@ -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')]
Expand Down Expand Up @@ -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')]
Expand Down Expand Up @@ -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')]
Expand All @@ -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')]
Expand Down
49 changes: 19 additions & 30 deletions src/Glpi/Api/HL/Controller/CoreController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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],
]
]
]
]
]
];
}
Expand All @@ -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'
]
]
)]
Expand All @@ -149,8 +140,6 @@ public function index(Request $request): Response
'api_versions' => Router::getAPIVersions()
];

$data['links'] = Router::getInstance()->getAllRoutePaths();

return new JSONResponse($data);
}

Expand Down
37 changes: 19 additions & 18 deletions src/Glpi/Api/HL/Controller/ITILController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<CommonITILObject> $itil_type */
Expand Down Expand Up @@ -211,7 +227,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) {
Expand All @@ -221,7 +236,9 @@ public static function getRawKnownSchemas(): array
}
return [];
},
'ref' => 'TeamMember'
'type' => Doc\Schema::TYPE_OBJECT,
'properties' => $schemas['TeamMember']['properties'],
'x-full-schema' => 'TeamMember',
]
];
}
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading