diff --git a/config/routes.php b/config/routes.php index 972163c6..16bcd971 100644 --- a/config/routes.php +++ b/config/routes.php @@ -121,6 +121,7 @@ $route->addGroup( $route->addRoute(['OPTIONS'], '[/{resource:.+}]', 'Api\IndexController@options'); $route->get('', 'Api\IndexController@indexV0'); $route->get('/openapi', 'Api\IndexController@openApiV0'); + $route->get('/info', 'Api\IndexController@info'); $route->get('/angeltypes', 'Api\AngelTypeController@index'); $route->get('/angeltypes/{angeltype_id:\d+}/shifts', 'Api\ShiftsController@entriesByAngeltype'); diff --git a/resources/api/openapi.yml b/resources/api/openapi.yml index 32935d56..a485927c 100644 --- a/resources/api/openapi.yml +++ b/resources/api/openapi.yml @@ -21,8 +21,12 @@ servers: description: Your local dev instance tags: + - name: api + description: API related - name: angeltype description: Angeltypes + - name: event + description: Event information - name: location description: Event locations - name: news @@ -105,11 +109,6 @@ components: required: - confirmed - supporter - Error: - type: object - properties: - message: - type: string News: type: object properties: @@ -140,16 +139,9 @@ components: example: false description: True if the news should be highlightet and shown on the dashboard created_at: - type: string - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T13:37:42.000000Z + $ref: '#/components/schemas/DateTime' updated_at: - type: string - nullable: true - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T23:00:00.000000Z + $ref: '#/components/schemas/DateTimeOptional' url: type: string example: https://example.com/news/42 @@ -197,31 +189,17 @@ components: Shift description, should be added to the shift type description but might be empty. Normally contains additional information for a specific shift / task. starts_at: - type: string - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T14:00:00.000000Z + $ref: '#/components/schemas/DateTime' ends_at: - type: string - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T16:00:00.000000Z23 + $ref: '#/components/schemas/DateTime' location: $ref: '#/components/schemas/Location' shift_type: $ref: '#/components/schemas/ShiftType' created_at: - type: string - nullable: true - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T13:37:42.000000Z + $ref: '#/components/schemas/DateTimeOptional' updated_at: - type: string - nullable: true - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T23:00:00.000000Z + $ref: '#/components/schemas/DateTimeOptional' entries: type: array description: Can be empty (for example on Schedule import of unused room) @@ -341,17 +319,9 @@ components: type: object properties: planned_arrival: - type: string - nullable: true - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-13T00:00:00.000000Z + $ref: '#/components/schemas/DateTimeOptional' planned_departure: - type: string - nullable: true - format: date-time - description: DateTime in ISO-8601 format - example: 2023-05-23T00:00:00.000000Z + $ref: '#/components/schemas/DateTimeOptional' arrival: type: string nullable: true @@ -375,6 +345,77 @@ components: - language - arrived + DateTime: + type: string + format: date-time + description: DateTime in ISO-8601 format + example: 2023-05-23T00:00:00.000000Z + DateTimeOptional: + type: string + format: date-time + description: DateTime in ISO-8601 format + example: 2023-05-23T00:00:00.000000Z + nullable: true + Error: + type: object + properties: + message: + type: string + EventInfo: + type: object + properties: + api: + type: string + description: API version for easier version detection + example: 1.2.3 + spec: + type: string + description: Link to OpenAPI specification + example: https://galactic-help.example/api/v1.2.3/openapi + name: + type: string + example: 42. Galactic Conglomeration Congress + app_name: + type: string + example: Engelsystem + url: + type: string + description: URL to be used when linking to the application + example: https://galactic-help.example + timezone: + type: string + example: Europe/Berlin + description: Timezone of the event + buildup: + type: object + properties: + start: + $ref: '#/components/schemas/DateTimeOptional' + end: + $ref: '#/components/schemas/DateTimeOptional' + required: + - start + - end + teardown: + type: object + properties: + start: + $ref: '#/components/schemas/DateTimeOptional' + end: + $ref: '#/components/schemas/DateTimeOptional' + required: + - start + - end + required: + - api + - spec + - name + - app_name + - url + - timezone + - buildup + - teardown + security: - bearerAuth: [ ] @@ -609,6 +650,28 @@ paths: '404': $ref: '#/components/responses/NotFoundError' + /info: + get: + tags: + - event + summary: Get event information + responses: + '200': + description: Ok + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/EventInfo' + '401': + $ref: '#/components/responses/UnauthorizedError' + '403': + $ref: '#/components/responses/ForbiddenError' + '404': + $ref: '#/components/responses/NotFoundError' + /openapi: get: tags: diff --git a/src/Controllers/Api/IndexController.php b/src/Controllers/Api/IndexController.php index 2b304408..6f6c8776 100644 --- a/src/Controllers/Api/IndexController.php +++ b/src/Controllers/Api/IndexController.php @@ -21,7 +21,7 @@ class IndexController extends ApiController return $this->response ->withContent(json_encode([ 'versions' => [ - '0.0.1-beta' => '/v0-beta', + $this->getApiSpecV0()->info->version => '/v0-beta', ], ])); } @@ -53,6 +53,32 @@ class IndexController extends ApiController return $this->response->withContent(json_encode($data)); } + public function info(): Response + { + $config = config(); + $schema = $this->getApiSpecV0(); + + $data = ['data' => [ + 'api' => $schema->info->version, + 'spec' => url('/api/v0-beta/openapi'), + 'name' => (string) $config->get('name'), + 'app_name' => (string) $config->get('app_name'), + 'url' => url('/'), + 'timezone' => (string) $config->get('timezone'), + 'buildup' => [ + 'start' => $config->get('buildup_start'), + 'end' => $config->get('buildup_end'), + ], + 'teardown' => [ + 'start' => $config->get('teardown_start'), + 'end' => $config->get('teardown_end'), + ], + ]]; + + return $this->response + ->withContent(json_encode($data)); + } + public function options(): Response { // Respond to browser preflight options requests diff --git a/tests/Unit/Controllers/Api/IndexControllerTest.php b/tests/Unit/Controllers/Api/IndexControllerTest.php index 339838a5..b0d80b4a 100644 --- a/tests/Unit/Controllers/Api/IndexControllerTest.php +++ b/tests/Unit/Controllers/Api/IndexControllerTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Engelsystem\Test\Unit\Controllers\Api; +use Engelsystem\Config\Config; use Engelsystem\Controllers\Api\IndexController; use Engelsystem\Http\Response; @@ -63,6 +64,52 @@ class IndexControllerTest extends ApiBaseControllerTest $this->assertArrayHasKey('info', $data); } + /** + * @covers \Engelsystem\Controllers\Api\IndexController::info + */ + public function testInfo(): void + { + $config = new Config(['name' => 'TestEvent', 'app_name' => 'TestSystem', 'timezone' => 'UTC']); + $this->app->instance('config', $config); + + $controller = new IndexController(new Response()); + + $response = $controller->info(); + $this->validateApiResponse('/info', 'get', $response); + + $this->assertEquals(['application/json'], $response->getHeader('content-type')); + $this->assertJson($response->getContent()); + + $data = json_decode($response->getContent(), true); + $this->assertArrayHasKey('data', $data); + $data = $data['data']; + $this->assertArrayHasKey('api', $data); + $this->assertArrayHasKey('timezone', $data); + $this->assertEquals('UTC', $data['timezone']); + } + + /** + * @covers \Engelsystem\Controllers\Api\IndexController::info + */ + public function testInfoNotConfigured(): void + { + $config = new Config([]); + $this->app->instance('config', $config); + + $controller = new IndexController(new Response()); + + $response = $controller->info(); + $this->validateApiResponse('/info', 'get', $response); + + $this->assertEquals(['application/json'], $response->getHeader('content-type')); + $this->assertJson($response->getContent()); + + $data = json_decode($response->getContent(), true); + $this->assertArrayHasKey('data', $data); + $this->assertArrayHasKey('name', $data['data']); + $this->assertEquals('', $data['data']['name']); + } + /** * @covers \Engelsystem\Controllers\Api\IndexController::options */