diff --git a/blog-api/README.md b/blog-api/README.md index dd6a209b..ca15f6d6 100644 --- a/blog-api/README.md +++ b/blog-api/README.md @@ -46,7 +46,7 @@ Authorization is performed via the `X-Api-Key` header. ## API documentation -API documentation is available at `/docs`. It is built from OpenAPI annotations (`@OA`). +API documentation is available at `/docs`. It is built from OpenAPI attributes (`#[OA\ ... ]`). See [Swagger-PHP documentation](https://zircote.github.io/swagger-php/Getting-started.html#write-annotations) for details on how to annotate your code. diff --git a/blog-api/composer.json b/blog-api/composer.json index 2338ba77..9f933538 100644 --- a/blog-api/composer.json +++ b/blog-api/composer.json @@ -41,7 +41,8 @@ "yiisoft/cache": "^3.0", "yiisoft/cache-file": "^3.0", "yiisoft/config": "^1.0", - "yiisoft/data": "^1.0", + "yiisoft/data": "dev-master", + "yiisoft/data-cycle": "dev-master", "yiisoft/data-response": "^2.0", "yiisoft/definitions": "^3.0", "yiisoft/di": "^1.0", @@ -49,9 +50,9 @@ "yiisoft/factory": "^1.0", "yiisoft/files": "^2.0", "yiisoft/http": "^1.2", - "yiisoft/hydrator-validator": "dev-master", + "yiisoft/hydrator-validator": "^2.0", "yiisoft/injector": "^1.0", - "yiisoft/input-http": "dev-master", + "yiisoft/input-http": "^1.0", "yiisoft/log": "^2.0", "yiisoft/log-target-file": "^3.0", "yiisoft/middleware-dispatcher": "^5.1", @@ -64,12 +65,12 @@ "yiisoft/user": "^2.0", "yiisoft/validator": "^1.0", "yiisoft/yii-console": "^2.0", - "yiisoft/yii-cycle": "dev-master", + "yiisoft/yii-cycle": "^1.0", "yiisoft/yii-debug": "^3.0@dev", "yiisoft/yii-event": "^2.0", "yiisoft/yii-http": "^1.0", "yiisoft/yii-middleware": "^1.0", - "yiisoft/yii-queue": "3.0.x-dev", + "yiisoft/queue": "3.0.x-dev", "yiisoft/yii-runner-console": "^2.0", "yiisoft/yii-runner-http": "^2.0", "yiisoft/yii-swagger": "^2.0" diff --git a/blog-api/config/common/params.php b/blog-api/config/common/params.php index e1530e39..98e8d888 100644 --- a/blog-api/config/common/params.php +++ b/blog-api/config/common/params.php @@ -5,15 +5,16 @@ use App\Queue\LoggingAuthorizationHandler; use Cycle\Database\Config\SQLite\FileConnectionConfig; use Cycle\Database\Config\SQLiteDriverConfig; +use Cycle\Schema\Provider\PhpFileSchemaProvider; use Yiisoft\ErrorHandler\Middleware\ErrorCatcher; +use Yiisoft\RequestProvider\RequestCatcherMiddleware; use Yiisoft\Router\Middleware\Router; use Yiisoft\Yii\Cycle\Schema\Conveyor\AttributedSchemaConveyor; use Yiisoft\Yii\Cycle\Schema\Provider\FromConveyorSchemaProvider; -use Yiisoft\Yii\Cycle\Schema\Provider\PhpFileSchemaProvider; use Yiisoft\Yii\Cycle\Schema\SchemaProviderInterface; use Yiisoft\Yii\Middleware\Locale; use Yiisoft\Yii\Middleware\Subfolder; -use Yiisoft\Yii\Queue\Adapter\SynchronousAdapter; +use Yiisoft\Queue\Adapter\SynchronousAdapter; return [ 'locale' => [ @@ -25,6 +26,7 @@ ], 'supportEmail' => 'support@example.com', 'middlewares' => [ + RequestCatcherMiddleware::class, ErrorCatcher::class, Subfolder::class, Locale::class, @@ -137,7 +139,7 @@ ], ], - 'yiisoft/yii-queue' => [ + 'yiisoft/queue' => [ 'handlers' => [ LoggingAuthorizationHandler::NAME => [LoggingAuthorizationHandler::class, 'handle'], ], diff --git a/blog-api/config/common/routes.php b/blog-api/config/common/routes.php index 2d39e0ab..82bf0769 100644 --- a/blog-api/config/common/routes.php +++ b/blog-api/config/common/routes.php @@ -63,6 +63,6 @@ Route::get('/openapi.json') ->middleware(FormatDataResponseAsJson::class) ->middleware(CorsAllowAll::class) - ->action([SwaggerJson::class, 'handle']), + ->action([SwaggerJson::class, 'process']), ), ]; diff --git a/blog-api/config/web/di/middleware-dispatcher.php b/blog-api/config/web/di/middleware-dispatcher.php index c9e2a159..d60af7bc 100644 --- a/blog-api/config/web/di/middleware-dispatcher.php +++ b/blog-api/config/web/di/middleware-dispatcher.php @@ -8,7 +8,6 @@ use Yiisoft\Definitions\Reference; use Yiisoft\Input\Http\HydratorAttributeParametersResolver; -use Yiisoft\Input\Http\Request\Catcher\RequestCatcherParametersResolver; use Yiisoft\Input\Http\RequestInputParametersResolver; use Yiisoft\Middleware\Dispatcher\CompositeParametersResolver; use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface; @@ -17,7 +16,6 @@ ParametersResolverInterface::class => [ 'class' => CompositeParametersResolver::class, '__construct()' => [ - Reference::to(RequestCatcherParametersResolver::class), Reference::to(HydratorAttributeParametersResolver::class), Reference::to(RequestInputParametersResolver::class), ], diff --git a/blog-api/src/Auth/AuthController.php b/blog-api/src/Auth/AuthController.php index d28d20d5..de3a7924 100644 --- a/blog-api/src/Auth/AuthController.php +++ b/blog-api/src/Auth/AuthController.php @@ -6,23 +6,12 @@ use App\User\UserRequest; use App\User\UserService; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; use Psr\Http\Message\ResponseInterface; use Yiisoft\DataResponse\DataResponseFactoryInterface as ResponseFactory; -/** - * @OA\Tag( - * name="auth", - * description="Authentication" - * ) - * - * @OA\SecurityScheme( - * securityScheme="ApiKey", - * type="apiKey", - * in="header", - * name="X-Api-Key" - * ) - */ +#[OA\Tag(name: 'auth', description: 'Authentication')] +#[OA\SecurityScheme(securityScheme: 'ApiKey', type: 'apiKey', name: 'X-Api-Key', in: 'header')] final class AuthController { private ResponseFactory $responseFactory; @@ -36,51 +25,42 @@ public function __construct( $this->userService = $userService; } - /** - * @OA\Post( - * tags={"auth"}, - * path="/auth/", - * summary="Authenticate by params", - * description="", - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property(property="token", format="string", example="uap4X5Bd7078lxIFvxAflcGAa5D95iSSZkNjg3XFrE2EBRBlbj"), - * ), - * ), - * }, - * ) - * ), - * - * @OA\Response( - * response="400", - * description="Bad request", - * - * @OA\JsonContent(ref="#/components/schemas/BadResponse") - * ), - * - * @OA\RequestBody( - * required=true, - * - * @OA\MediaType( - * mediaType="application/json", - * - * @OA\Schema(ref="#/components/schemas/AuthRequest"), - * ), - * ), - * ) - */ + #[OA\Post( + path: '/auth/', + description: '', + summary: 'Authenticate by params', + requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/AuthRequest'), + ] + )), + tags: ['auth'], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property( + property: 'data', + properties: [ + new OA\Property(property: 'token', type: 'string', example: 'uap4X5Bd7078lxIFvxAflcGAa5D95iSSZkNjg3XFrE2EBRBlbj'), + ], + type: 'object' + ), + ]), + ] + ) + ), + new OA\Response( + response: '400', + description: 'Bad request', + content: new OA\JsonContent(ref: '#/components/schemas/BadResponse') + ), + ] + )] public function login(AuthRequest $request): ResponseInterface { return $this->responseFactory->createResponse( @@ -95,29 +75,25 @@ public function login(AuthRequest $request): ResponseInterface ); } - /** - * @OA\Post( - * tags={"auth"}, - * path="/logout/", - * summary="Logout", - * description="", - * security={{"ApiKey": {}}}, - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent(ref="#/components/schemas/Response") - * ), - * - * @OA\Response( - * response="400", - * description="Bad request", - * - * @OA\JsonContent(ref="#/components/schemas/BadResponse") - * ), - * ) - */ + #[OA\Post( + path: '/logout/', + description: '', + summary: 'Logout', + security: [new OA\SecurityScheme(ref: '#/components/securitySchemes/ApiKey')], + tags: ['auth'], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/Response') + ), + new OA\Response( + response: '400', + description: 'Bad request', + content: new OA\JsonContent(ref: '#/components/schemas/BadResponse') + ), + ] + )] public function logout(UserRequest $request): ResponseInterface { $this->userService->logout($request->getUser()); diff --git a/blog-api/src/Auth/AuthRequest.php b/blog-api/src/Auth/AuthRequest.php index 49e76426..d72bdfbb 100644 --- a/blog-api/src/Auth/AuthRequest.php +++ b/blog-api/src/Auth/AuthRequest.php @@ -4,20 +4,19 @@ namespace App\Auth; -use OpenApi\Annotations as OA; use Yiisoft\Input\Http\Attribute\Parameter\Body; use Yiisoft\Input\Http\RequestInputInterface; use Yiisoft\Validator\Rule\Required; use Yiisoft\Validator\RulesProviderInterface; - -/** - * @OA\Schema( - * schema="AuthRequest", - * - * @OA\Property(example="Opal1144", property="login", format="string"), - * @OA\Property(example="Opal1144", property="password", format="string"), - * ) - */ +use OpenApi\Attributes as OA; + +#[OA\Schema( + schema: 'AuthRequest', + properties: [ + new OA\Property(property: 'login', type: 'string', example: 'Opal1144'), + new OA\Property(property: 'password', type: 'string', example: 'Opal1144'), + ] +)] final class AuthRequest implements RequestInputInterface, RulesProviderInterface { #[Body('login')] diff --git a/blog-api/src/Blog/BlogController.php b/blog-api/src/Blog/BlogController.php index 7ca82918..88e051b7 100644 --- a/blog-api/src/Blog/BlogController.php +++ b/blog-api/src/Blog/BlogController.php @@ -6,29 +6,14 @@ use App\Formatter\PaginatorFormatter; use App\User\UserRequest; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; use Psr\Http\Message\ResponseInterface as Response; use Yiisoft\DataResponse\DataResponseFactoryInterface; use Yiisoft\Input\Http\Attribute\Parameter\Query; -use Yiisoft\Hydrator\Temp\RouteArgument; +use Yiisoft\Router\HydratorAttribute\RouteArgument; -/** - * @OA\Tag( - * name="blog", - * description="Blog" - * ) - * - * @OA\Parameter( - * - * @OA\Schema( - * type="int", - * example="2" - * ), - * in="query", - * name="page", - * parameter="PageRequest" - * ) - */ +#[OA\Tag(name: 'blog', description: 'Blog')] +#[OA\Parameter(parameter: 'PageRequest', name: 'page', in: 'query', schema: new OA\Schema(type: 'int', example: '2'))] final class BlogController { private DataResponseFactoryInterface $responseFactory; @@ -51,47 +36,36 @@ public function __construct( $this->blogService = $blogService; } - /** - * @OA\Get( - * tags={"blog"}, - * path="/blog/", - * summary="Returns paginated blog posts", - * description="", - * - * @OA\Parameter(ref="#/components/parameters/PageRequest"), - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property( - * property="posts", - * type="array", - * - * @OA\Items(ref="#/components/schemas/Post") - * ), - * - * @OA\Property( - * property="paginator", - * type="object", - * ref="#/components/schemas/Paginator" - * ), - * ), - * ), - * }, - * ) - * ), - * ) - */ + #[OA\Get( + path: '/blog/', + description: '', + summary: 'Returns paginated blog posts', + tags: ['blog'], + parameters: [ + new OA\Parameter(ref: '#/components/parameters/PageRequest'), + ], + responses: [ + new OA\Response( + response:'200', + description:'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property( + property: 'data', + properties: [ + new OA\Property(property: 'posts', type: 'array', items: new OA\Items(ref:'#/components/schemas/Post')), + new OA\Property(property: 'paginator', ref: '#/components/schemas/Paginator', type: 'object'), + ], + type: 'object' + ), + ]), + ] + ), + ), + ] + )] public function index(PaginatorFormatter $paginatorFormatter, #[Query('page')] int $page = 1): Response { $paginator = $this->blogService->getPosts($page); @@ -108,63 +82,46 @@ public function index(PaginatorFormatter $paginatorFormatter, #[Query('page')] i ); } - /** - * @OA\Get( - * tags={"blog"}, - * path="/blog/{id}", - * summary="Returns a post with a given ID", - * description="", - * - * @OA\Parameter( - * - * @OA\Schema(type="int", example="2"), - * in="path", - * name="id", - * parameter="id" - * ), - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property( - * property="post", - * type="object", - * ref="#/components/schemas/Post" - * ), - * ), - * ), - * }, - * ) - * ), - * - * @OA\Response( - * response="404", - * description="Not found", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/BadResponse"), - * @OA\Schema( - * - * @OA\Property(property="error_message", example="Entity not found"), - * @OA\Property(property="error_code", nullable=true, example=404) - * ), - * }, - * ) - * ), - * ) - */ + #[OA\Get( + path: '/blog/{id}', + description: '', + summary: 'Returns a post with a given ID', + tags: ['blog'], + parameters: [ + new OA\Parameter(parameter: 'id', name: 'id', in: 'path', schema: new OA\Schema(type: 'int', example: '2')), + ], + responses: [ + new OA\Response( + response:'200', + description:'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property( + property: 'data', + properties: [ + new OA\Property(property: 'post', ref: '#/components/schemas/Post', type: 'object'), + ], + type: 'object' + ), + ]), + ] + ), + ), + new OA\Response( + response: '404', + description: 'Not found', + content: new OA\JsonContent(allOf: [ + new OA\Schema(ref: '#/components/schemas/BadResponse'), + new OA\Schema(properties: [ + new OA\Property(property:'error_message', example:'Entity not found'), + new OA\Property(property: 'error_code', example: 404, nullable: true), + ]), + ]) + ), + ] + )] public function view(#[RouteArgument('id')] int $id): Response { return $this->responseFactory->createResponse( @@ -176,34 +133,30 @@ public function view(#[RouteArgument('id')] int $id): Response ); } - /** - * @OA\Post( - * tags={"blog"}, - * path="/blog", - * summary="Creates a blog post", - * description="", - * security={{"ApiKey": {}}}, - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * ref="#/components/schemas/Response" - * ) - * ), - * - * @OA\RequestBody( - * required=true, - * - * @OA\MediaType( - * mediaType="application/json", - * - * @OA\Schema(ref="#/components/schemas/EditPostRequest"), - * ), - * ), - * ) - */ + #[OA\Post( + path: '/blog/', + description: '', + summary: 'Creates a blog post', + security: [new OA\SecurityScheme(ref: '#/components/securitySchemes/ApiKey')], + requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/EditPostRequest'), + ] + )), + tags: ['blog'], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/Response') + ), + new OA\Response( + response: '400', + description: 'Bad request', + content: new OA\JsonContent(ref: '#/components/schemas/BadResponse') + ), + ] + )] public function create(EditPostRequest $postRequest, UserRequest $userRequest): Response { $post = $this->postBuilder->build(new Post(), $postRequest); @@ -214,42 +167,33 @@ public function create(EditPostRequest $postRequest, UserRequest $userRequest): return $this->responseFactory->createResponse(); } - /** - * @OA\Put( - * tags={"blog"}, - * path="/blog/{id}", - * summary="Updates a blog post with a given ID", - * description="", - * security={{"ApiKey": {}}}, - * - * @OA\Parameter( - * - * @OA\Schema(type="int", example="2"), - * in="path", - * name="id", - * parameter="id" - * ), - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * ref="#/components/schemas/Response" - * ) - * ), - * - * @OA\RequestBody( - * required=true, - * - * @OA\MediaType( - * mediaType="application/json", - * - * @OA\Schema(ref="#/components/schemas/EditPostRequest"), - * ), - * ) - * ) - */ + #[OA\Put( + path: '/blog/{id}', + description: '', + summary: 'Updates a blog post with a given ID', + security: [new OA\SecurityScheme(ref: '#/components/securitySchemes/ApiKey')], + requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/EditPostRequest'), + ] + )), + tags: ['blog'], + parameters: [ + new OA\Parameter(parameter: 'id', name: 'id', in: 'path', schema: new OA\Schema(type: 'int', example: '2')), + ], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/Response') + ), + new OA\Response( + response: '400', + description: 'Bad request', + content: new OA\JsonContent(ref: '#/components/schemas/BadResponse') + ), + ] + )] public function update(EditPostRequest $postRequest, #[RouteArgument('id')] int $id): Response { $post = $this->postBuilder->build( diff --git a/blog-api/src/Blog/EditPostRequest.php b/blog-api/src/Blog/EditPostRequest.php index 61d1f977..f7f24838 100644 --- a/blog-api/src/Blog/EditPostRequest.php +++ b/blog-api/src/Blog/EditPostRequest.php @@ -4,25 +4,24 @@ namespace App\Blog; -use OpenApi\Annotations as OA; use Yiisoft\Hydrator\Validator\Attribute\Validate; use Yiisoft\Input\Http\AbstractInput; use Yiisoft\Input\Http\Attribute\Parameter\Body; -use Yiisoft\Hydrator\Temp\RouteArgument; +use Yiisoft\Router\HydratorAttribute\RouteArgument; use Yiisoft\Validator\Result; use Yiisoft\Validator\Rule\Length; use Yiisoft\Validator\Rule\Required; use Yiisoft\Validator\RulesProviderInterface; +use OpenApi\Attributes as OA; -/** - * @OA\Schema( - * schema="EditPostRequest", - * - * @OA\Property(example="Title post", property="title", format="string"), - * @OA\Property(example="Text post", property="text", format="string"), - * @OA\Property(example=1, property="status", format="int"), - * ) - */ +#[OA\Schema( + schema: 'EditPostRequest', + properties: [ + new OA\Property(property: 'title', type: 'string', example: 'Title post'), + new OA\Property(property: 'text', type: 'string', example: 'Text post'), + new OA\Property(property: 'status', type: 'int', example: '1'), + ] +)] final class EditPostRequest extends AbstractInput implements RulesProviderInterface { #[RouteArgument('id')] diff --git a/blog-api/src/Blog/PostFormatter.php b/blog-api/src/Blog/PostFormatter.php index 3193d4bc..db8d1bfe 100644 --- a/blog-api/src/Blog/PostFormatter.php +++ b/blog-api/src/Blog/PostFormatter.php @@ -4,17 +4,16 @@ namespace App\Blog; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; -/** - * @OA\Schema( - * schema="Post", - * - * @OA\Property(example="100", property="id", format="int"), - * @OA\Property(example="Title", property="title", format="string"), - * @OA\Property(example="Text", property="content", format="string"), - * ) - */ +#[OA\Schema( + schema: 'Post', + properties: [ + new OA\Property(property: 'id', type: 'int', example: '100'), + new OA\Property(property: 'title', type: 'string', example: 'Title'), + new OA\Property(property: 'content', type: 'string', example: 'Text'), + ] +)] final class PostFormatter { public function format(Post $post): array diff --git a/blog-api/src/Blog/PostRepository.php b/blog-api/src/Blog/PostRepository.php index fec64bbf..c27838be 100644 --- a/blog-api/src/Blog/PostRepository.php +++ b/blog-api/src/Blog/PostRepository.php @@ -7,7 +7,7 @@ use Cycle\ORM\ORMInterface; use Cycle\ORM\Select; use Cycle\ORM\Transaction; -use Yiisoft\Yii\Cycle\Data\Reader\EntityReader; +use Yiisoft\Data\Cycle\Reader\EntityReader; final class PostRepository extends Select\Repository { diff --git a/blog-api/src/Dto/ApiResponseData.php b/blog-api/src/Dto/ApiResponseData.php index 65e85c9e..114e934e 100644 --- a/blog-api/src/Dto/ApiResponseData.php +++ b/blog-api/src/Dto/ApiResponseData.php @@ -4,70 +4,53 @@ namespace App\Dto; -use OpenApi\Annotations as OA; - -/** - * @OA\Schema( - * schema="Response", - * ) - * @OA\Schema( - * schema="BadResponse", - * allOf={ - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="status", - * example="failed", - * ), - * @OA\Property(property="error_message", example="Error description message"), - * @OA\Property(property="error_code", nullable=true, example=400), - * @OA\Property( - * property="data", - * example=null - * ), - * ) - * } - * ) - */ +use OpenApi\Attributes as OA; + +#[OA\Schema( + schema: 'Response' +)] +#[OA\Schema( + schema: 'BadResponse', + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property(property: 'status', example: 'failed'), + new OA\Property(property: 'error_message', example: 'Error description message'), + new OA\Property(property: 'error_code', example: '400', nullable: true), + new OA\Property(property: 'data', example: null), + ]), + ] +)] final class ApiResponseData { - /** - * @OA\Property( - * property="status", - * format="string", - * example="success", - * enum={"success", "failed"} - * ) - */ + #[OA\Property( + property: 'status', + format: 'string', + enum: ['success', 'failed'], + example: 'success' + )] private string $status = ''; - /** - * @OA\Property( - * property="error_message", - * format="string", - * example="" - * ) - */ + #[OA\Property( + property: 'error_message', + format: 'string', + example: '' + )] private string $errorMessage = ''; - /** - * @OA\Property( - * property="error_code", - * format="integer", - * nullable=true, - * example=null - * ) - */ + #[OA\Property( + property: 'error_code', + format: 'integer', + example: null, + nullable: true + )] private ?int $errorCode = null; - /** - * @OA\Property( - * property="data", - * type="object", - * nullable=true, - * ) - */ + #[OA\Property( + property: 'data', + type: 'object', + nullable: true + )] private ?array $data = null; public function getStatus(): string diff --git a/blog-api/src/Formatter/PaginatorFormatter.php b/blog-api/src/Formatter/PaginatorFormatter.php index 983c6eef..88266c18 100644 --- a/blog-api/src/Formatter/PaginatorFormatter.php +++ b/blog-api/src/Formatter/PaginatorFormatter.php @@ -4,18 +4,17 @@ namespace App\Formatter; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; use Yiisoft\Data\Paginator\OffsetPaginator; -/** - * @OA\Schema( - * schema="Paginator", - * - * @OA\Property(example="10", property="pageSize", format="int"), - * @OA\Property(example="1", property="currentPage", format="int"), - * @OA\Property(example="3", property="totalPages", format="int"), - * ) - */ +#[OA\Schema( + schema: 'Paginator', + properties: [ + new OA\Property(property: 'pageSize', type: 'int', example: '10'), + new OA\Property(property: 'currentPage', type: 'int', example: '1'), + new OA\Property(property: 'totalPages', type: 'int', example: '3'), + ] +)] final class PaginatorFormatter { public function format(OffsetPaginator $paginator): array diff --git a/blog-api/src/InfoController.php b/blog-api/src/InfoController.php index 3ce6fafb..5dbe11ff 100644 --- a/blog-api/src/InfoController.php +++ b/blog-api/src/InfoController.php @@ -4,55 +4,39 @@ namespace App; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; use Psr\Http\Message\ResponseInterface; use Yiisoft\DataResponse\DataResponseFactoryInterface; -/** - * @OA\Info(title="Yii API application", version="1.0") - */ +#[OA\Info(version: '1.0', title: 'Yii API application')] class InfoController { public function __construct(private VersionProvider $versionProvider) { } - /** - * @OA\Get( - * path="/", - * summary="Returns info about the API", - * description="", - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property( - * property="version", - * type="string", - * example="3.0" - * ), - * @OA\Property( - * property="author", - * type="string", - * example="yiisoft" - * ), - * ), - * ), - * }, - * ) - * ), - * ) - */ + #[OA\Get( + path: '/', + description: '', + summary: 'Returns info about the API', + responses: [ + new OA\Response( + response:'200', + description:'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property(property: 'data', properties: [ + new OA\Property(property: 'version', type: 'string', example: '3.0'), + new OA\Property(property: 'author', type: 'string', example: 'yiisoft'), + ], type: 'object'), + ]), + ] + ), + ), + ] + )] public function index(DataResponseFactoryInterface $responseFactory): ResponseInterface { return $responseFactory->createResponse(['version' => $this->versionProvider->version, 'author' => 'yiisoft']); diff --git a/blog-api/src/Queue/UserLoggedInMessage.php b/blog-api/src/Queue/UserLoggedInMessage.php index 99f879e3..141e7d3b 100644 --- a/blog-api/src/Queue/UserLoggedInMessage.php +++ b/blog-api/src/Queue/UserLoggedInMessage.php @@ -4,7 +4,7 @@ namespace App\Queue; -use Yiisoft\Yii\Queue\Message\MessageInterface; +use Yiisoft\Queue\Message\MessageInterface; final class UserLoggedInMessage implements MessageInterface { diff --git a/blog-api/src/User/UserController.php b/blog-api/src/User/UserController.php index c02bbf4e..cbb63502 100644 --- a/blog-api/src/User/UserController.php +++ b/blog-api/src/User/UserController.php @@ -6,17 +6,12 @@ use App\Exception\NotFoundException; use App\RestControllerTrait; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; use Psr\Http\Message\ResponseInterface; use Yiisoft\DataResponse\DataResponseFactoryInterface; -use Yiisoft\Hydrator\Temp\RouteArgument; +use Yiisoft\Router\HydratorAttribute\RouteArgument; -/** - * @OA\Tag( - * name="user", - * description="Users" - * ) - */ +#[OA\Tag(name: 'user', description: 'User')] final class UserController { use RestControllerTrait; @@ -35,40 +30,37 @@ public function __construct( $this->userFormatter = $userFormatter; } - /** - * @OA\Get( - * tags={"user"}, - * path="/users", - * summary="Returns paginated users", - * description="", - * security={{"ApiKey": {}}}, - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property( - * property="users", - * type="array", - * - * @OA\Items(ref="#/components/schemas/User") - * ), - * ), - * ), - * }, - * ) - * ), - * ) - */ + #[OA\Get( + path: '/users/', + description: '', + summary: 'Returns paginated users', + security: [new OA\SecurityScheme(ref: '#/components/securitySchemes/ApiKey')], + tags: ['user'], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property( + property: 'data', + properties: [ + new OA\Property( + property: 'user', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/User') + ), + ], + type: 'object' + ), + ]), + ] + ) + ), + ] + )] public function list(): ResponseInterface { $dataReader = $this->userRepository->findAllOrderByLogin(); @@ -84,47 +76,36 @@ public function list(): ResponseInterface ); } - /** - * @OA\Get( - * tags={"user"}, - * path="/users/{id}", - * summary="Returns a user with a given ID", - * description="", - * security={{"ApiKey": {}}}, - * - * @OA\Parameter( - * - * @OA\Schema(type="int", example="2"), - * in="path", - * name="id", - * parameter="id" - * ), - * - * @OA\Response( - * response="200", - * description="Success", - * - * @OA\JsonContent( - * allOf={ - * - * @OA\Schema(ref="#/components/schemas/Response"), - * @OA\Schema( - * - * @OA\Property( - * property="data", - * type="object", - * @OA\Property( - * property="user", - * type="object", - * ref="#/components/schemas/User" - * ), - * ), - * ), - * }, - * ) - * ), - * ) - */ + #[OA\Get( + path: '/users/{id}', + description: '', + summary: 'Returns a user with a given ID', + security: [new OA\SecurityScheme(ref: '#/components/securitySchemes/ApiKey')], + tags: ['user'], + parameters: [ + new OA\Parameter(parameter: 'id', name: 'id', in: 'path', example: 2), + ], + responses: [ + new OA\Response( + response: '200', + description: 'Success', + content: new OA\JsonContent( + allOf: [ + new OA\Schema(ref: '#/components/schemas/Response'), + new OA\Schema(properties: [ + new OA\Property( + property: 'data', + properties: [ + new OA\Property(property: 'user', ref: '#/components/schemas/User', type: 'object'), + ], + type: 'object' + ), + ]), + ] + ) + ), + ] + )] public function get(#[RouteArgument('id')] int $id): ResponseInterface { /** diff --git a/blog-api/src/User/UserFormatter.php b/blog-api/src/User/UserFormatter.php index 115280f4..8a04fe59 100644 --- a/blog-api/src/User/UserFormatter.php +++ b/blog-api/src/User/UserFormatter.php @@ -4,16 +4,15 @@ namespace App\User; -use OpenApi\Annotations as OA; +use OpenApi\Attributes as OA; -/** - * @OA\Schema( - * schema="User", - * - * @OA\Property(example="UserName", property="login", format="string"), - * @OA\Property(example="13.12.2020 00:04:20", property="created_at", format="string"), - * ) - */ +#[OA\Schema( + schema: 'User', + properties: [ + new OA\Property(property: 'login', type: 'string', example: 'UserName'), + new OA\Property(property: 'created_at', type: 'string', example: '13.12.2020 00:04:20'), + ] +)] final class UserFormatter { public function format(User $user): array diff --git a/blog-api/src/User/UserRepository.php b/blog-api/src/User/UserRepository.php index 590b8665..acd5d750 100644 --- a/blog-api/src/User/UserRepository.php +++ b/blog-api/src/User/UserRepository.php @@ -10,8 +10,8 @@ use Yiisoft\Auth\IdentityInterface; use Yiisoft\Auth\IdentityRepositoryInterface; use Yiisoft\Auth\IdentityWithTokenRepositoryInterface; +use Yiisoft\Data\Cycle\Reader\EntityReader; use Yiisoft\Data\Reader\Sort; -use Yiisoft\Yii\Cycle\Data\Reader\EntityReader; final class UserRepository extends Select\Repository implements IdentityWithTokenRepositoryInterface, IdentityRepositoryInterface { diff --git a/blog-api/src/User/UserService.php b/blog-api/src/User/UserService.php index 43ee5a39..f5df91d5 100644 --- a/blog-api/src/User/UserService.php +++ b/blog-api/src/User/UserService.php @@ -11,7 +11,7 @@ use Yiisoft\Auth\IdentityRepositoryInterface; use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\User\CurrentUser; -use Yiisoft\Yii\Queue\QueueFactoryInterface; +use Yiisoft\Queue\QueueFactoryInterface; final class UserService {