API: Add /users/{id}/angeltypes, updated openapi formatting

This commit is contained in:
Igor Scheller 2023-11-03 23:27:31 +01:00 committed by Michael Weimann
parent 497c1772f7
commit 5b237febf8
4 changed files with 125 additions and 9 deletions

View File

@ -123,6 +123,7 @@ $route->addGroup(
$route->get('/news', 'Api\NewsController@index');
$route->get('/locations', 'Api\LocationsController@index');
$route->get('/locations/{location_id:\d+}/shifts', 'Api\ShiftsController@entriesByLocation');
$route->get('/users/{user_id:\d+}/angeltypes', 'Api\AngelTypeController@ofUser');
$route->addRoute(
['POST', 'PUT', 'DELETE', 'PATCH'],

View File

@ -3,7 +3,7 @@ openapi: 3.0.3
info:
version: 0.0.1-beta
title: Engelsystem
description: |
description: >
This API is as stable as a **beta** version might be.
It could burst into flames and morph into a monster at any second!
(But we try to keep it somewhat consistent, at least during events).
@ -25,6 +25,8 @@ tags:
description: News and meeting announcements
- name: shift
description: Event shifts and location
- name: user
description: User information
components:
securitySchemes:
@ -81,6 +83,24 @@ components:
- name
- description
- url
UserAngelType:
allOf:
- $ref: '#/components/schemas/AngelType'
- type: object
properties:
confirmed:
type: boolean
example: true
description: >
If the user is confirmed
(either by the angeltype not requiring confirmation, being a supporter or being confirmed by one).
supporter:
type: boolean
example: false
description: If the user is a supporter of the angeltype.
required:
- confirmed
- supporter
Error:
type: object
properties:
@ -168,9 +188,9 @@ components:
example: Cleanup the venue
description:
type: string
example: You clean up the venue after the event, its fun, we promise!
description: |
Shift description, should be added to the shift type description as it might be empty.
example: You clean up the venue after the event. Its fun, we promise!
description: >
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
@ -206,7 +226,7 @@ components:
url:
type: string
example: https://example.com/shifts/42
description: Direct link of the shift
description: Direct link to the shift
required:
- id
- title
@ -230,9 +250,10 @@ components:
$ref: '#/components/schemas/AngelType'
needs:
type: integer
description: |
Number of users needed for the shift.
Can be more than users count when not full, less when overbooked or 0 when additional users have been added.
description: >
Number of users needed for the shift of the given type.
Will be more than users count when not full, less when overbooked
or 0 when only additional users with given type have been added.
example: 3
required:
- users
@ -402,3 +423,35 @@ paths:
$ref: '#/components/responses/ForbiddenError'
'404':
$ref: '#/components/responses/NotFoundError'
/users/{id}/angeltypes:
parameters:
- name: id
in: path
required: true
description: The user identifier
example: 42
schema:
type: integer
get:
tags:
- user
summary: Get the users angel types
responses:
'200':
description: Ok
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/UserAngelType'
'401':
$ref: '#/components/responses/UnauthorizedError'
'403':
$ref: '#/components/responses/ForbiddenError'
'404':
$ref: '#/components/responses/NotFoundError'

View File

@ -4,8 +4,10 @@ declare(strict_types=1);
namespace Engelsystem\Controllers\Api;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\User\User;
class AngelTypeController extends ApiController
{
@ -16,11 +18,43 @@ class AngelTypeController extends ApiController
->get(['id', 'name', 'description']);
$models->map(function (AngelType $model): void {
$model->url = $this->url->to('/angeltypes', ['action' => 'view', 'angeltype_id' => $model->id]);
$model->url = $this->getUrl($model);
});
$data = ['data' => $models];
return $this->response
->withContent(json_encode($data));
}
public function ofUser(Request $request): Response
{
$id = (int) $request->getAttribute('user_id');
$model = User::findOrFail($id);
$models = $model->userAngelTypes()->get([
'angel_types.id',
'angel_types.name',
'angel_types.description',
'angel_types.restricted',
]);
$data = [];
$models->map(function (AngelType $model) use (&$data): void {
$model->confirmed = !$model->restricted || $model->pivot->supporter || $model->pivot->confirm_user_id;
$model->supporter = $model->pivot->supporter;
$model->url = $this->getUrl($model);
$modelData = $model->attributesToArray();
unset($modelData['restricted']);
$data[] = $modelData;
});
$data = ['data' => $data];
return $this->response
->withContent(json_encode($data));
}
protected function getUrl(AngelType $model): string
{
return $this->url->to('/angeltypes', ['action' => 'view', 'angeltype_id' => $model->id]);
}
}

View File

@ -5,13 +5,17 @@ declare(strict_types=1);
namespace Engelsystem\Test\Unit\Controllers\Api;
use Engelsystem\Controllers\Api\AngelTypeController;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType;
class AngelTypeControllerTest extends ApiBaseControllerTest
{
/**
* @covers \Engelsystem\Controllers\Api\AngelTypeController::index
* @covers \Engelsystem\Controllers\Api\AngelTypeController::getUrl
*/
public function testIndex(): void
{
@ -33,4 +37,28 @@ class AngelTypeControllerTest extends ApiBaseControllerTest
return $item['name'] == $items->first()->getAttribute('name');
}));
}
/**
* @covers \Engelsystem\Controllers\Api\AngelTypeController::ofUser
*/
public function testOfUser(): void
{
$this->initDatabase();
$user = User::factory()->create();
$items = UserAngelType::factory(3)->create(['user_id' => $user->id]);
$controller = new AngelTypeController(new Response(), $this->url);
$response = $controller->ofUser(new Request([], [], ['user_id' => $user->id]));
$this->validateApiResponse('/users/{id}/angeltypes', '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->assertCount(3, $data['data']);
$this->assertCount(1, collect($data['data'])->filter(function ($item) use ($items) {
return $item['name'] == $items->first()->angelType->name;
}));
}
}