From 9bb57cad75e32717608efa86c5cb205ad893ce23 Mon Sep 17 00:00:00 2001 From: mattamon Date: Wed, 10 Apr 2024 11:47:39 +0200 Subject: [PATCH] Make use of attributes --- .../Parameters/Path/IdParameter.php | 22 ++++++ .../Parameters/Query/LimitParameter.php | 22 ++++++ .../Parameters/Query/PageParameter.php | 22 ++++++ .../Request/CredentialsRequestBody.php | 20 +++++ src/Attributes/Request/TokenRequestBody.php | 20 +++++ .../Request/TranslationRequestBody.php | 20 +++++ src/Attributes/Response/SuccessResponse.php | 19 +++++ .../Response/UnauthorizedResponse.php | 21 +++++ .../Api/Assets/CollectionController.php | 52 +++---------- src/Controller/Api/Assets/GetController.php | 39 ++-------- .../Authorization/AuthorizationController.php | 78 ++++--------------- src/Controller/Api/TranslationController.php | 40 ++-------- src/Dto/Collection.php | 10 +++ src/Dto/Translation.php | 1 + src/Dto/Unauthorized.php | 1 + src/Security/Trait/RequestTrait.php | 2 +- src/Service/OpenApiService.php | 6 +- 17 files changed, 227 insertions(+), 168 deletions(-) create mode 100644 src/Attributes/Parameters/Path/IdParameter.php create mode 100644 src/Attributes/Parameters/Query/LimitParameter.php create mode 100644 src/Attributes/Parameters/Query/PageParameter.php create mode 100644 src/Attributes/Request/CredentialsRequestBody.php create mode 100644 src/Attributes/Request/TokenRequestBody.php create mode 100644 src/Attributes/Request/TranslationRequestBody.php create mode 100644 src/Attributes/Response/SuccessResponse.php create mode 100644 src/Attributes/Response/UnauthorizedResponse.php diff --git a/src/Attributes/Parameters/Path/IdParameter.php b/src/Attributes/Parameters/Path/IdParameter.php new file mode 100644 index 000000000..b4bd7f652 --- /dev/null +++ b/src/Attributes/Parameters/Path/IdParameter.php @@ -0,0 +1,22 @@ + [], ], ], - tags: ['Assets'] + tags: ['Assets'], )] - #[QueryParameter( - name: 'page', - description: 'Page number', - in: 'query', - required: true, - schema: new Schema(type: 'integer', example: 1), - example: 1 - )] - #[QueryParameter( - name: 'limit', - description: 'Number of items per page', - in: 'query', - required: true, - schema: new Schema(type: 'integer', example: 1), - example: 10 - )] - #[Response( - response: 200, + #[PageParameter] + #[LimitParameter] + #[SuccessResponse( description: 'Paginated assets with total count as header param', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Asset::class, type: 'object') - ), - ] - )] - #[Response( - response: 401, - description: 'Unauthorized', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Unauthorized::class, type: 'object') - ), - ] + content: new JsonContent(ref: Asset::class) )] + #[UnauthorizedResponse] public function getAssets(#[MapQueryString] Collection $collection): JsonResponse { diff --git a/src/Controller/Api/Assets/GetController.php b/src/Controller/Api/Assets/GetController.php index 48b7c768c..00c033a3e 100644 --- a/src/Controller/Api/Assets/GetController.php +++ b/src/Controller/Api/Assets/GetController.php @@ -17,13 +17,12 @@ namespace Pimcore\Bundle\StudioApiBundle\Controller\Api\Assets; use OpenApi\Attributes\Get; -use OpenApi\Attributes\MediaType; -use OpenApi\Attributes\PathParameter; -use OpenApi\Attributes\Response; -use OpenApi\Attributes\Schema; +use OpenApi\Attributes\JsonContent; +use Pimcore\Bundle\StudioApiBundle\Attributes\Parameters\Path\IdParameter; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\SuccessResponse; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\UnauthorizedResponse; use Pimcore\Bundle\StudioApiBundle\Controller\Api\AbstractApiController; use Pimcore\Bundle\StudioApiBundle\Dto\Asset; -use Pimcore\Bundle\StudioApiBundle\Dto\Unauthorized; use Pimcore\Bundle\StudioApiBundle\Service\AssetSearchServiceInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; @@ -52,34 +51,12 @@ public function __construct( ], tags: ['Assets'] )] - #[PathParameter( - name: 'id', - description: 'Id of the asset', - in: 'path', - required: true, - schema: new Schema(type: 'integer', example: 83), - example: 83 - )] - #[Response( - response: 200, + #[IdParameter(type: 'asset')] + #[SuccessResponse( description: 'Paginated assets with total count as header param', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Asset::class, type: 'object') - ), - ] - )] - #[Response( - response: 401, - description: 'Unauthorized', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Unauthorized::class, type: 'object') - ), - ] + content: new JsonContent(ref: Asset::class) )] + #[UnauthorizedResponse] public function getAssets(int $id): JsonResponse { return $this->jsonResponse($this->assetSearchService->getAssetById($id)); diff --git a/src/Controller/Api/Authorization/AuthorizationController.php b/src/Controller/Api/Authorization/AuthorizationController.php index c23d1f85c..3ae48b23f 100644 --- a/src/Controller/Api/Authorization/AuthorizationController.php +++ b/src/Controller/Api/Authorization/AuthorizationController.php @@ -16,16 +16,16 @@ namespace Pimcore\Bundle\StudioApiBundle\Controller\Api\Authorization; -use OpenApi\Attributes\MediaType; +use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Post; -use OpenApi\Attributes\RequestBody; -use OpenApi\Attributes\Response; -use OpenApi\Attributes\Schema; +use Pimcore\Bundle\StudioApiBundle\Attributes\Request\CredentialsRequestBody; +use Pimcore\Bundle\StudioApiBundle\Attributes\Request\TokenRequestBody; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\SuccessResponse; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\UnauthorizedResponse; use Pimcore\Bundle\StudioApiBundle\Controller\Api\AbstractApiController; use Pimcore\Bundle\StudioApiBundle\Dto\Credentials; use Pimcore\Bundle\StudioApiBundle\Dto\Token; use Pimcore\Bundle\StudioApiBundle\Dto\Token\Refresh; -use Pimcore\Bundle\StudioApiBundle\Dto\Unauthorized; use Pimcore\Bundle\StudioApiBundle\Service\SecurityServiceInterface; use Pimcore\Bundle\StudioApiBundle\Service\TokenServiceInterface; use Pimcore\Security\User\User; @@ -50,35 +50,12 @@ public function __construct( summary: 'Login with user credentials and get access token', tags: ['Authorization'] )] - #[RequestBody( - required: true, - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Credentials::class, type: 'object') - ), - ] - )] - #[Response( - response: 200, - description: 'Key value pairs for given keys and locale', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Token::class, type: 'object') - ), - ] - )] - #[Response( - response: 403, - description: 'Unauthorized', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Unauthorized::class, type: 'object') - ), - ] + #[CredentialsRequestBody] + #[SuccessResponse( + description: 'Token, lifetime and user identifier', + content: new JsonContent(ref: Token::class) )] + #[UnauthorizedResponse] public function login(#[MapRequestPayload] Credentials $credentials): JsonResponse { /** @var User $user */ @@ -89,41 +66,18 @@ public function login(#[MapRequestPayload] Credentials $credentials): JsonRespon return $this->jsonResponse(new Token($token, $this->tokenService->getLifetime(), $user->getUserIdentifier())); } + #[Route('/refresh', name: 'pimcore_studio_api_refresh', methods: ['POST'])] #[POST( path: self::API_PATH . '/refresh', summary: 'Login with user credentials and get access token', tags: ['Authorization'] )] - #[RequestBody( - required: true, - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Refresh::class, type: 'object') - ), - ] + #[TokenRequestBody] + #[SuccessResponse( + description: 'Token, lifetime and user identifier', + content: new JsonContent(ref: Token::class) )] - #[Response( - response: 200, - description: 'Key value pairs for given keys and locale', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Token::class, type: 'object') - ), - ] - )] - #[Response( - response: 403, - description: 'Unauthorized', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Unauthorized::class, type: 'object') - ), - ] - )] - #[Route('/refresh', name: 'pimcore_studio_api_refresh', methods: ['POST'])] + #[UnauthorizedResponse] public function refresh(#[MapRequestPayload] Refresh $refresh): JsonResponse { $token = $this->tokenService->refreshToken($refresh->getToken()); diff --git a/src/Controller/Api/TranslationController.php b/src/Controller/Api/TranslationController.php index 10f364cae..4fb14c5e1 100644 --- a/src/Controller/Api/TranslationController.php +++ b/src/Controller/Api/TranslationController.php @@ -16,13 +16,12 @@ namespace Pimcore\Bundle\StudioApiBundle\Controller\Api; -use OpenApi\Attributes\MediaType; +use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Post; -use OpenApi\Attributes\RequestBody; -use OpenApi\Attributes\Response; -use OpenApi\Attributes\Schema; +use Pimcore\Bundle\StudioApiBundle\Attributes\Request\TranslationRequestBody; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\SuccessResponse; +use Pimcore\Bundle\StudioApiBundle\Attributes\Response\UnauthorizedResponse; use Pimcore\Bundle\StudioApiBundle\Dto\Translation; -use Pimcore\Bundle\StudioApiBundle\Dto\Unauthorized; use Pimcore\Bundle\StudioApiBundle\Service\TranslatorServiceInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; @@ -54,35 +53,12 @@ public function __construct( ], tags: ['Translation'] )] - #[RequestBody( - required: true, - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Translation::class, type: 'object') - ), - ] - )] - #[Response( - response: 200, + #[TranslationRequestBody] + #[SuccessResponse( description: 'Key value pairs for given keys and locale', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Translation::class, type: 'object') - ), - ] - )] - #[Response( - response: 401, - description: 'Unauthorized', - content:[ - new MediaType( - mediaType: 'application/json', - schema: new Schema(ref: Unauthorized::class, type: 'object') - ), - ] + content: new JsonContent(ref: Translation::class) )] + #[UnauthorizedResponse] public function getTranslations( #[MapRequestPayload] Translation $translation, ): JsonResponse { diff --git a/src/Dto/Collection.php b/src/Dto/Collection.php index 2b83b6868..7050b4120 100644 --- a/src/Dto/Collection.php +++ b/src/Dto/Collection.php @@ -16,10 +16,20 @@ namespace Pimcore\Bundle\StudioApiBundle\Dto; +use OpenApi\Attributes\Property; +use OpenApi\Attributes\Schema; + +#[Schema( + schema: 'Collection', + title: 'Collection', + type: 'object' +)] final readonly class Collection { public function __construct( + #[Property(description: 'page', type: 'integer', example: 1)] private int $page = 1, + #[Property(description: 'limit', type: 'integer', example: 10)] private int $limit = 10 ) { } diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php index ba669892c..031f5d60c 100644 --- a/src/Dto/Translation.php +++ b/src/Dto/Translation.php @@ -25,6 +25,7 @@ * @internal */ #[Schema( + schema: 'Translation', title: 'Translation', description: 'Translation Scheme for API', type: 'object' diff --git a/src/Dto/Unauthorized.php b/src/Dto/Unauthorized.php index 0551032c0..b58002090 100644 --- a/src/Dto/Unauthorized.php +++ b/src/Dto/Unauthorized.php @@ -20,6 +20,7 @@ use OpenApi\Attributes\Schema; #[Schema( + schema: 'Unauthorized', title: 'Unauthorized', description: 'Bad credentials or missing token', type: 'object' diff --git a/src/Security/Trait/RequestTrait.php b/src/Security/Trait/RequestTrait.php index 8698af97d..96ad91958 100644 --- a/src/Security/Trait/RequestTrait.php +++ b/src/Security/Trait/RequestTrait.php @@ -35,7 +35,7 @@ private function getAuthToken(Request $request): string $authToken = $request->headers->get(self::AUTHORIZATION_HEADER); if($authToken === null) { throw new NotAuthorizedException( - 403, + 401, 'Full authentication is required.' ); } diff --git a/src/Service/OpenApiService.php b/src/Service/OpenApiService.php index ed437ad8c..82e6158ac 100644 --- a/src/Service/OpenApiService.php +++ b/src/Service/OpenApiService.php @@ -23,9 +23,11 @@ final class OpenApiService implements OpenApiServiceInterface private const PATH_PREFIX = __DIR__ . '/../../'; private const PATHS = [ - self::PATH_PREFIX . 'src/Controller/Api', - self::PATH_PREFIX . 'src/Dto/', + self::PATH_PREFIX . 'src/Attributes/', self::PATH_PREFIX . 'src/Config/', + self::PATH_PREFIX . 'src/Controller/Api/', + self::PATH_PREFIX . 'src/Dto/', + ]; public function getConfig(): OpenApi