API: Add urls to response, configure required fields
This commit is contained in:
parent
a5cebc8535
commit
ea93e27a9d
|
@ -63,6 +63,17 @@ components:
|
|||
name:
|
||||
type: string
|
||||
example: Angel
|
||||
description:
|
||||
type: string
|
||||
example: Meta-Group of all registered Angels
|
||||
url:
|
||||
type: string
|
||||
example: https://example.com/news/42
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- description
|
||||
- url
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -105,6 +116,19 @@ components:
|
|||
format: date-time
|
||||
description: DateTime in ISO-8601 format
|
||||
example: 2023-05-13T23:00:00.000000Z
|
||||
url:
|
||||
type: string
|
||||
example: https://example.com/news/42
|
||||
required:
|
||||
- id
|
||||
- title
|
||||
- text
|
||||
- is_meeting
|
||||
- is_pinned
|
||||
- is_highlighted
|
||||
- created_at
|
||||
- updated_at
|
||||
- url
|
||||
Room:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -114,6 +138,13 @@ components:
|
|||
name:
|
||||
type: string
|
||||
example: Heaven
|
||||
url:
|
||||
type: string
|
||||
example: https://example.com/rooms/42
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- url
|
||||
Shift:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -156,6 +187,21 @@ components:
|
|||
format: date-time
|
||||
description: DateTime in ISO-8601 format
|
||||
example: 2023-05-13T23:00:00.000000Z
|
||||
url:
|
||||
type: string
|
||||
example: https://example.com/shifts/42
|
||||
required:
|
||||
- id
|
||||
- title
|
||||
- description
|
||||
- start
|
||||
- end
|
||||
- entries
|
||||
- room
|
||||
- shift_type
|
||||
- created_at
|
||||
- updated_at
|
||||
- url
|
||||
ShiftEntry:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -163,6 +209,9 @@ components:
|
|||
$ref: '#/components/schemas/User'
|
||||
type:
|
||||
$ref: '#/components/schemas/AngelType'
|
||||
required:
|
||||
- user
|
||||
- type
|
||||
ShiftType:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -175,6 +224,10 @@ components:
|
|||
description:
|
||||
type: string
|
||||
example: This is a generic build-up shift, mostly involving heavy lifting.
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- description
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -207,6 +260,17 @@ components:
|
|||
type: string
|
||||
nullable: true
|
||||
example: 1234567890
|
||||
url:
|
||||
type: string
|
||||
example: https://example.com/users/42
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- first_name
|
||||
- last_name
|
||||
- pronoun
|
||||
- contact
|
||||
- url
|
||||
|
||||
security:
|
||||
- bearerAuth: [ ]
|
||||
|
|
|
@ -11,11 +11,15 @@ class AngelTypeController extends ApiController
|
|||
{
|
||||
public function index(): Response
|
||||
{
|
||||
$news = AngelType::query()
|
||||
$models = AngelType::query()
|
||||
->orderBy('name')
|
||||
->get(['id', 'name']);
|
||||
->get(['id', 'name', 'description']);
|
||||
|
||||
$data = ['data' => $news];
|
||||
$models->map(function (AngelType $model): void {
|
||||
$model->url = $this->url->to('/angeltypes', ['action' => 'view', 'angeltype_id' => $model->id]);
|
||||
});
|
||||
|
||||
$data = ['data' => $models];
|
||||
return $this->response
|
||||
->withContent(json_encode($data));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Engelsystem\Controllers\Api;
|
|||
|
||||
use Engelsystem\Controllers\BaseController;
|
||||
use Engelsystem\Http\Response;
|
||||
use Engelsystem\Http\UrlGeneratorInterface;
|
||||
|
||||
abstract class ApiController extends BaseController
|
||||
{
|
||||
|
@ -13,7 +14,7 @@ abstract class ApiController extends BaseController
|
|||
'api',
|
||||
];
|
||||
|
||||
public function __construct(protected Response $response)
|
||||
public function __construct(protected Response $response, protected UrlGeneratorInterface $url)
|
||||
{
|
||||
$this->response = $this->response
|
||||
->withHeader('content-type', 'application/json')
|
||||
|
|
|
@ -11,12 +11,16 @@ class NewsController extends ApiController
|
|||
{
|
||||
public function index(): Response
|
||||
{
|
||||
$news = News::query()
|
||||
$models = News::query()
|
||||
->orderByDesc('updated_at')
|
||||
->orderByDesc('created_at')
|
||||
->get(['id', 'title', 'text', 'is_meeting', 'is_pinned', 'is_highlighted', 'created_at', 'updated_at']);
|
||||
|
||||
$data = ['data' => $news];
|
||||
$models->map(function (News $model): void {
|
||||
$model->url = $this->url->to('/news/' . $model->id);
|
||||
});
|
||||
|
||||
$data = ['data' => $models];
|
||||
return $this->response
|
||||
->withContent(json_encode($data));
|
||||
}
|
||||
|
|
|
@ -11,11 +11,15 @@ class RoomsController extends ApiController
|
|||
{
|
||||
public function index(): Response
|
||||
{
|
||||
$news = Room::query()
|
||||
$models = Room::query()
|
||||
->orderBy('name')
|
||||
->get(['id', 'name']);
|
||||
|
||||
$data = ['data' => $news];
|
||||
$models->map(function (Room $model): void {
|
||||
$model->url = $this->url->to('/rooms', ['action' => 'view', 'room_id' => $model->id]);
|
||||
});
|
||||
|
||||
$data = ['data' => $models];
|
||||
return $this->response
|
||||
->withContent(json_encode($data));
|
||||
}
|
||||
|
|
|
@ -37,14 +37,24 @@ class ShiftsController extends ApiController
|
|||
'last_name' => $user->personalData->last_name,
|
||||
'pronoun' => $user->personalData->pronoun,
|
||||
'contact' => $user->contact->only(['dect', 'mobile']),
|
||||
'url' => $this->url->to('/users', ['action' => 'view', 'user_id' => $user->id]),
|
||||
];
|
||||
|
||||
$angelTypeData = $entry->angelType->only(['id', 'name']);
|
||||
$angelTypeData['url'] = $this->url->to(
|
||||
'/angeltypes',
|
||||
['action' => 'view', 'angeltype_id' => $entry->angelType->id]
|
||||
);
|
||||
|
||||
$entries[] = [
|
||||
'user' => $userData,
|
||||
'type' => $entry->angelType->only(['id', 'name']),
|
||||
'type' => $angelTypeData,
|
||||
];
|
||||
}
|
||||
|
||||
$roomData = $room->only(['id', 'name']);
|
||||
$roomData['url'] = $this->url->to('/rooms', ['action' => 'view', 'room_id' => $room->id]);
|
||||
|
||||
$shiftEntries[] = [
|
||||
'id' => $shift->id,
|
||||
'title' => $shift->title,
|
||||
|
@ -52,10 +62,11 @@ class ShiftsController extends ApiController
|
|||
'start' => $shift->start,
|
||||
'end' => $shift->end,
|
||||
'entries' => $entries,
|
||||
'room' => $room->only(['id', 'name']),
|
||||
'room' => $roomData,
|
||||
'shift_type' => $shift->shiftType->only(['id', 'name', 'description']),
|
||||
'created_at' => $shift->created_at,
|
||||
'updated_at' => $shift->updated_at,
|
||||
'url' => $this->url->to('/shifts', ['action' => 'view', 'shift_id' => $shift->id]),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class AngelTypeControllerTest extends ApiBaseControllerTest
|
|||
$this->initDatabase();
|
||||
AngelType::factory(3)->create();
|
||||
|
||||
$controller = new AngelTypeController(new Response());
|
||||
$controller = new AngelTypeController(new Response(), $this->url);
|
||||
|
||||
$response = $controller->index();
|
||||
$this->validateApiResponse('/angeltypes', 'get', $response);
|
||||
|
|
|
@ -4,15 +4,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace Engelsystem\Test\Unit\Controllers\Api;
|
||||
|
||||
use Engelsystem\Http\UrlGeneratorInterface;
|
||||
use Engelsystem\Test\Unit\Controllers\ControllerTest as TestCase;
|
||||
use League\OpenAPIValidation\PSR7\OperationAddress as OpenApiAddress;
|
||||
use League\OpenAPIValidation\PSR7\ResponseValidator as OpenApiResponseValidator;
|
||||
use League\OpenAPIValidation\PSR7\ValidatorBuilder as OpenApiValidatorBuilder;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
abstract class ApiBaseControllerTest extends TestCase
|
||||
{
|
||||
protected OpenApiResponseValidator $validator;
|
||||
protected UrlGeneratorInterface $url;
|
||||
|
||||
protected function validateApiResponse(string $path, string $method, ResponseInterface $response): void
|
||||
{
|
||||
|
@ -29,5 +32,15 @@ abstract class ApiBaseControllerTest extends TestCase
|
|||
$this->validator = (new OpenApiValidatorBuilder())
|
||||
->fromYamlFile($openApiDefinition)
|
||||
->getResponseValidator();
|
||||
|
||||
/** @var UrlGeneratorInterface|MockObject $url */
|
||||
$url = $this->getMockForAbstractClass(UrlGeneratorInterface::class);
|
||||
$url->expects($this->any())
|
||||
->method('to')
|
||||
->willReturnCallback(function (string $path, array $params): string {
|
||||
$query = http_build_query($params);
|
||||
return $path . ($query ? '?' . $query : '');
|
||||
});
|
||||
$this->url = $url;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class ApiControllerTest extends ApiBaseControllerTest
|
|||
*/
|
||||
public function testConstruct(): void
|
||||
{
|
||||
$controller = new class (new Response('{"some":"json"}')) extends ApiController {
|
||||
$controller = new class (new Response('{"some":"json"}'), $this->url) extends ApiController {
|
||||
public function getResponse(): Response
|
||||
{
|
||||
return $this->response;
|
||||
|
|
|
@ -15,7 +15,7 @@ class IndexControllerTest extends ApiBaseControllerTest
|
|||
*/
|
||||
public function testIndex(): void
|
||||
{
|
||||
$controller = new IndexController(new Response());
|
||||
$controller = new IndexController(new Response(), $this->url);
|
||||
$response = $controller->index();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
@ -32,7 +32,7 @@ class IndexControllerTest extends ApiBaseControllerTest
|
|||
*/
|
||||
public function testIndexV0(): void
|
||||
{
|
||||
$controller = new IndexController(new Response());
|
||||
$controller = new IndexController(new Response(), $this->url);
|
||||
$response = $controller->indexV0();
|
||||
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
@ -48,7 +48,7 @@ class IndexControllerTest extends ApiBaseControllerTest
|
|||
*/
|
||||
public function testOptions(): void
|
||||
{
|
||||
$controller = new IndexController(new Response());
|
||||
$controller = new IndexController(new Response(), $this->url);
|
||||
$response = $controller->options();
|
||||
|
||||
$this->assertEquals(204, $response->getStatusCode());
|
||||
|
@ -61,7 +61,7 @@ class IndexControllerTest extends ApiBaseControllerTest
|
|||
*/
|
||||
public function testNotImplemented(): void
|
||||
{
|
||||
$controller = new IndexController(new Response());
|
||||
$controller = new IndexController(new Response(), $this->url);
|
||||
$response = $controller->notImplemented();
|
||||
|
||||
$this->assertEquals(501, $response->getStatusCode());
|
||||
|
|
|
@ -17,7 +17,7 @@ class NewsControllerTest extends ApiBaseControllerTest
|
|||
{
|
||||
News::factory(3)->create();
|
||||
|
||||
$controller = new NewsController(new Response());
|
||||
$controller = new NewsController(new Response(), $this->url);
|
||||
|
||||
$response = $controller->index();
|
||||
$this->validateApiResponse('/news', 'get', $response);
|
||||
|
|
|
@ -18,7 +18,7 @@ class RoomsControllerTest extends ApiBaseControllerTest
|
|||
$this->initDatabase();
|
||||
Room::factory(3)->create();
|
||||
|
||||
$controller = new RoomsController(new Response());
|
||||
$controller = new RoomsController(new Response(), $this->url);
|
||||
|
||||
$response = $controller->index();
|
||||
$this->validateApiResponse('/rooms', 'get', $response);
|
||||
|
|
|
@ -40,10 +40,10 @@ class ShiftsControllerTest extends ApiBaseControllerTest
|
|||
$request = new Request();
|
||||
$request = $request->withAttribute('room_id', $room->id);
|
||||
|
||||
$controller = new ShiftsController(new Response());
|
||||
$controller = new ShiftsController(new Response(), $this->url);
|
||||
|
||||
$response = $controller->entriesByRoom($request);
|
||||
$this->validateApiResponse('/rooms', 'get', $response);
|
||||
$this->validateApiResponse('/rooms/{id}/shifts', 'get', $response);
|
||||
|
||||
$this->assertEquals(['application/json'], $response->getHeader('content-type'));
|
||||
$this->assertJson($response->getContent());
|
||||
|
|
Loading…
Reference in New Issue