Skip to content

Commit a3d899d

Browse files
committed
hlapi add some missing timeline properties
1 parent 7e112ef commit a3d899d

File tree

7 files changed

+136
-39
lines changed

7 files changed

+136
-39
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ The present file will list all changes made to the project; according to the
66
## [11.0.1] unreleased
77

88
### Added
9+
- `timeline_position`, `source_item_id`, and `source_of_item_id` properties for the related timeline item schemas in the High-Level API.
910

1011
### Changed
12+
- Added High-Level API version 2.1. Make sure you are pinning your requests to a specific version (Ex: `/api.php/v2.0`) if needed to exclude endpoints/properties added in later versions. See version pinning in the getting started documentation `/api.php/getting-started`.
13+
- High-Level API responses for not found routes now correctly return a body including the standard error properties (status, title, detail). This is not controlled by the API version.
1114

1215
### Deprecated
1316

src/Glpi/Api/HL/Controller/CoreController.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ public function showDocumentation(Request $request): Response
163163
$swagger_content .= Html::script('/lib/swagger-ui.js');
164164
$swagger_content .= Html::css('/lib/swagger-ui.css');
165165
$favicon = Html::getPrefixedUrl('/pics/favicon.ico');
166-
$doc_json_path = $CFG_GLPI['root_doc'] . '/api.php/doc.json';
166+
$api_version = $this->getAPIVersion($request);
167+
$doc_json_path = $CFG_GLPI['root_doc'] . '/api.php/v' . $api_version . '/doc.json';
167168
$swagger_content .= <<<HTML
168169
<link rel="shortcut icon" type="images/x-icon" href="$favicon" />
169170
</head>
@@ -285,7 +286,7 @@ private function getAllowedMethodsForMatchedRoute(Request $request): array
285286
)]
286287
public function defaultRoute(Request $request): Response
287288
{
288-
return new JSONResponse(null, 404);
289+
return self::getNotFoundErrorResponse();
289290
}
290291

291292
#[Route(path: '/{req}', methods: ['OPTIONS'], requirements: ['req' => '.*'], priority: -1, security_level: Route::SECURITY_NONE)]
@@ -299,7 +300,7 @@ public function defaultOptionsRoute(Request $request): Response
299300
$authenticated = Session::getLoginUserID() !== false;
300301
$allowed_methods = $authenticated ? $this->getAllowedMethodsForMatchedRoute($request) : ['GET', 'POST', 'PATCH', 'PUT', "DELETE"];
301302
if (count($allowed_methods) === 0) {
302-
return new JSONResponse(null, 404);
303+
return self::getNotFoundErrorResponse();
303304
}
304305
$response_headers = [];
305306
if ($authenticated) {

src/Glpi/Api/HL/Controller/ITILController.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,24 @@ public static function getRawKnownSchemas(): array
421421
];
422422
}
423423

424+
$timeline_position_enum = [
425+
CommonITILObject::NO_TIMELINE,
426+
CommonITILObject::TIMELINE_NOTSET,
427+
CommonITILObject::TIMELINE_LEFT,
428+
CommonITILObject::TIMELINE_MIDLEFT,
429+
CommonITILObject::TIMELINE_MIDRIGHT,
430+
CommonITILObject::TIMELINE_RIGHT,
431+
];
432+
$timeline_position_description = <<<EOT
433+
The position in the timeline.
434+
- 0: No timeline
435+
- 1: Not set
436+
- 2: Left
437+
- 3: Mid left
438+
- 4: Mid right
439+
- 5: Right
440+
EOT;
441+
424442
$base_task_schema = [
425443
'type' => Doc\Schema::TYPE_OBJECT,
426444
'x-rights-conditions' => [ // Object-level extra permissions
@@ -464,13 +482,31 @@ public static function getRawKnownSchemas(): array
464482
EOT,
465483
],
466484
'category' => self::getDropdownTypeSchema(class: TaskCategory::class, full_schema: 'TaskCategory'),
485+
'timeline_position' => [
486+
'x-version-introduced' => '2.1.0',
487+
'type' => Doc\Schema::TYPE_NUMBER,
488+
'enum' => $timeline_position_enum,
489+
'description' => $timeline_position_description,
490+
],
467491
],
468492
];
469493

470494
$schemas['TicketTask'] = $base_task_schema;
471495
$schemas['TicketTask']['x-version-introduced'] = '2.0';
472496
$schemas['TicketTask']['x-itemtype'] = TicketTask::class;
473497
$schemas['TicketTask']['properties'][Ticket::getForeignKeyField()] = ['type' => Doc\Schema::TYPE_INTEGER, 'format' => Doc\Schema::FORMAT_INTEGER_INT64];
498+
$schemas['TicketTask']['properties']['source_item_id'] = [
499+
'x-version-introduced' => '2.1.0',
500+
'type' => Doc\Schema::TYPE_INTEGER,
501+
'format' => Doc\Schema::FORMAT_INTEGER_INT64,
502+
'x-field' => 'sourceitems_id',
503+
];
504+
$schemas['TicketTask']['properties']['source_of_item_id'] = [
505+
'x-version-introduced' => '2.1.0',
506+
'type' => Doc\Schema::TYPE_INTEGER,
507+
'format' => Doc\Schema::FORMAT_INTEGER_INT64,
508+
'x-field' => 'sourceof_items_id',
509+
];
474510

475511
$schemas['ChangeTask'] = $base_task_schema;
476512
$schemas['ChangeTask']['x-version-introduced'] = '2.0';
@@ -561,6 +597,24 @@ public static function getRawKnownSchemas(): array
561597
'request_type' => self::getDropdownTypeSchema(RequestType::class, full_schema: 'RequestType'),
562598
'date_creation' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME],
563599
'date_mod' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME],
600+
'timeline_position' => [
601+
'x-version-introduced' => '2.1.0',
602+
'type' => Doc\Schema::TYPE_NUMBER,
603+
'enum' => $timeline_position_enum,
604+
'description' => $timeline_position_description,
605+
],
606+
'source_item_id' => [
607+
'x-version-introduced' => '2.1.0',
608+
'type' => Doc\Schema::TYPE_INTEGER,
609+
'format' => Doc\Schema::FORMAT_INTEGER_INT64,
610+
'x-field' => 'sourceitems_id',
611+
],
612+
'source_of_item_id' => [
613+
'x-version-introduced' => '2.1.0',
614+
'type' => Doc\Schema::TYPE_INTEGER,
615+
'format' => Doc\Schema::FORMAT_INTEGER_INT64,
616+
'x-field' => 'sourceof_items_id',
617+
],
564618
],
565619
];
566620

@@ -625,6 +679,12 @@ public static function getRawKnownSchemas(): array
625679
],
626680
'submission_date' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME],
627681
'approval_date' => ['type' => Doc\Schema::TYPE_STRING, 'format' => Doc\Schema::FORMAT_STRING_DATE_TIME, 'x-field' => 'validation_date'],
682+
'timeline_position' => [
683+
'x-version-introduced' => '2.1.0',
684+
'type' => Doc\Schema::TYPE_NUMBER,
685+
'enum' => $timeline_position_enum,
686+
'description' => $timeline_position_description,
687+
],
628688
],
629689
];
630690

src/Glpi/Api/HL/Controller/ManagementController.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
use BusinessCriticity;
4141
use Cluster;
4242
use CommonDBTM;
43+
use CommonITILObject;
4344
use Contact;
4445
use Contract;
4546
use Database;
@@ -366,6 +367,27 @@ protected static function getRawKnownSchemas(): array
366367
'x-mapped-from' => 'documents_id',
367368
'x-mapper' => static fn($v) => $CFG_GLPI["root_doc"] . "/front/document.send.php?docid=" . $v,
368369
],
370+
'timeline_position' => [
371+
'x-version-introduced' => '2.1.0',
372+
'type' => Doc\Schema::TYPE_NUMBER,
373+
'enum' => [
374+
CommonITILObject::NO_TIMELINE,
375+
CommonITILObject::TIMELINE_NOTSET,
376+
CommonITILObject::TIMELINE_LEFT,
377+
CommonITILObject::TIMELINE_MIDLEFT,
378+
CommonITILObject::TIMELINE_MIDRIGHT,
379+
CommonITILObject::TIMELINE_RIGHT,
380+
],
381+
'description' => <<<EOT
382+
The position in the timeline.
383+
- 0: No timeline
384+
- 1: Not set
385+
- 2: Left
386+
- 3: Mid left
387+
- 4: Mid right
388+
- 5: Right
389+
EOT,
390+
],
369391
],
370392
];
371393

src/Glpi/Api/HL/OpenAPIGenerator.php

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ private function getInfo(): array
131131
return [
132132
'title' => 'GLPI High-Level REST API',
133133
'description' => $description,
134-
'version' => Router::API_VERSION,
134+
'version' => $this->api_version,
135135
'license' => [
136136
'name' => 'GNU General Public License v3 or later',
137137
'url' => 'https://www.gnu.org/licenses/gpl-3.0.html',
@@ -141,40 +141,36 @@ private function getInfo(): array
141141

142142
public static function getComponentSchemas(string $api_version): array
143143
{
144-
static $schemas = null;
145-
146-
if ($schemas === null) {
147-
$schemas = [];
148-
149-
$controllers = Router::getInstance()->getControllers();
150-
foreach ($controllers as $controller) {
151-
$known_schemas = $controller::getKnownSchemas($api_version);
152-
$short_name = (new ReflectionClass($controller))->getShortName();
153-
$controller_name = str_replace('Controller', '', $short_name);
154-
foreach ($known_schemas as $schema_name => $known_schema) {
155-
// Ignore schemas starting with an underscore. They are only used internally.
156-
if (str_starts_with($schema_name, '_')) {
157-
continue;
158-
}
159-
$calculated_name = $schema_name;
160-
if (isset($schemas[$schema_name])) {
161-
// For now, set the new calculated name to the short name of the controller + the schema name
162-
$calculated_name = $controller_name . ' - ' . $schema_name;
163-
// Change the existing schema name to its own calculated name
164-
$other_short_name = (new ReflectionClass($schemas[$schema_name]['x-controller']))->getShortName();
165-
$other_calculated_name = str_replace('Controller', '', $other_short_name) . ' - ' . $schema_name;
166-
$schemas[$other_calculated_name] = $schemas[$schema_name];
167-
unset($schemas[$schema_name]);
168-
}
169-
if (!isset($known_schema['description']) && isset($known_schema['x-itemtype'])) {
170-
/** @var class-string<CommonGLPI> $itemtype */
171-
$itemtype = $known_schema['x-itemtype'];
172-
$known_schema['description'] = $itemtype::getTypeName(1);
173-
}
174-
$schemas[$calculated_name] = $known_schema;
175-
$schemas[$calculated_name]['x-controller'] = $controller::class;
176-
$schemas[$calculated_name]['x-schemaname'] = $schema_name;
144+
$schemas = [];
145+
146+
$controllers = Router::getInstance()->getControllers();
147+
foreach ($controllers as $controller) {
148+
$known_schemas = $controller::getKnownSchemas($api_version);
149+
$short_name = (new ReflectionClass($controller))->getShortName();
150+
$controller_name = str_replace('Controller', '', $short_name);
151+
foreach ($known_schemas as $schema_name => $known_schema) {
152+
// Ignore schemas starting with an underscore. They are only used internally.
153+
if (str_starts_with($schema_name, '_')) {
154+
continue;
177155
}
156+
$calculated_name = $schema_name;
157+
if (isset($schemas[$schema_name])) {
158+
// For now, set the new calculated name to the short name of the controller + the schema name
159+
$calculated_name = $controller_name . ' - ' . $schema_name;
160+
// Change the existing schema name to its own calculated name
161+
$other_short_name = (new ReflectionClass($schemas[$schema_name]['x-controller']))->getShortName();
162+
$other_calculated_name = str_replace('Controller', '', $other_short_name) . ' - ' . $schema_name;
163+
$schemas[$other_calculated_name] = $schemas[$schema_name];
164+
unset($schemas[$schema_name]);
165+
}
166+
if (!isset($known_schema['description']) && isset($known_schema['x-itemtype'])) {
167+
/** @var class-string<CommonGLPI> $itemtype */
168+
$itemtype = $known_schema['x-itemtype'];
169+
$known_schema['description'] = $itemtype::getTypeName(1);
170+
}
171+
$schemas[$calculated_name] = $known_schema;
172+
$schemas[$calculated_name]['x-controller'] = $controller::class;
173+
$schemas[$calculated_name]['x-schemaname'] = $schema_name;
178174
}
179175
}
180176

@@ -255,6 +251,9 @@ public function getSchema(): array
255251
$paths = [];
256252

257253
foreach ($routes as $route_path) {
254+
if (!$route_path->matchesAPIVersion($this->api_version)) {
255+
continue;
256+
}
258257
/** @noinspection SlowArrayOperationsInLoopInspection */
259258
$paths = array_merge_recursive($paths, $this->getPathSchemas($route_path));
260259
}

src/Glpi/Api/HL/Router.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
class Router
9090
{
9191
/** @var string */
92-
public const API_VERSION = '2.0.0';
92+
public const API_VERSION = '2.1.0';
9393

9494
/**
9595
* @var AbstractController[]
@@ -165,9 +165,14 @@ public static function getAPIVersions(): array
165165
],
166166
[
167167
'api_version' => $current_version_major,
168-
'version' => self::API_VERSION,
168+
'version' => '2.0.0',
169169
'endpoint' => $CFG_GLPI['url_base'] . '/api.php/v2',
170170
],
171+
[
172+
'api_version' => $current_version_major,
173+
'version' => '2.1.0',
174+
'endpoint' => $CFG_GLPI['url_base'] . '/api.php/v2.1',
175+
],
171176
];
172177
}
173178

@@ -185,6 +190,12 @@ public static function normalizeAPIVersion(string $version): string
185190
{
186191
$versions = array_column(static::getAPIVersions(), 'version');
187192
$best_match = self::API_VERSION;
193+
// if not a full x.y.z version, add missing parts as 0
194+
$parts = explode('.', $version);
195+
while (count($parts) < 3) {
196+
$parts[] = '0';
197+
}
198+
$version = implode('.', $parts);
188199
if (in_array($version, $versions, true)) {
189200
// Exact match
190201
return $version;

tests/functional/Glpi/Api/HL/RouterTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public function testAllSchemasHaveVersioningInfo()
9090
public function testNormalizeAPIVersion()
9191
{
9292
$this->assertEquals('50.2.0', TestRouter::normalizeAPIVersion('50'));
93+
$this->assertEquals('50.0.0', TestRouter::normalizeAPIVersion('50.0'));
9394
$this->assertEquals('50.1.1', TestRouter::normalizeAPIVersion('50.1.1'));
9495
$this->assertEquals('50.1.2', TestRouter::normalizeAPIVersion('50.1'));
9596
$this->assertEquals('50.2.0', TestRouter::normalizeAPIVersion('50.2'));

0 commit comments

Comments
 (0)