From d1e031db55f3d31cab2e5b219e1d8554532fd7a9 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Wed, 26 Jun 2024 10:50:18 +0200 Subject: [PATCH 01/23] composer json with api-platform as dependency --- composer.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5724674da..884a56d9e 100644 --- a/composer.json +++ b/composer.json @@ -22,12 +22,13 @@ } }, "require": { - "php": " >=8.3", + "php": " ^8.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-simplexml": "*", "ext-xmlwriter": "*", + "api-platform/core": "3.3.x-dev", "hautelook/templated-uri-bundle": "^3.4", "ibexa/core": "~5.0.x-dev", "lexik/jwt-authentication-bundle": "^2.8", @@ -78,7 +79,15 @@ }, "extra": { "branch-alias": { - "dev-main": "5.0.x-dev" + "dev-main": "5.0.x-dev", + "dev-IBX-8335-api-platform": "5.0.x-dev" } - } + }, + "repositories": { + "api-platform/core": { + "type": "path", + "url": "~/Sites/openapi-forks/core" + } + }, + "minimum-stability": "dev" } From 071286d6cd6e3cef9cdf900caaa2510de9f1250a Mon Sep 17 00:00:00 2001 From: tischsoic Date: Wed, 26 Jun 2024 10:56:20 +0200 Subject: [PATCH 02/23] fix alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 884a56d9e..6afefaec1 100644 --- a/composer.json +++ b/composer.json @@ -80,7 +80,7 @@ "extra": { "branch-alias": { "dev-main": "5.0.x-dev", - "dev-IBX-8335-api-platform": "5.0.x-dev" + "dev-IBX-8335-full-api-platform": "5.0.x-dev" } }, "repositories": { From 010800cbbba51e5ec56c34c0a0a746903b40832d Mon Sep 17 00:00:00 2001 From: tischsoic Date: Mon, 1 Jul 2024 11:54:09 +0200 Subject: [PATCH 03/23] WIP --- ...ClassNameResourceNameCollectionFactory.php | 38 ++++++++++ src/bundle/ApiPlatform/OpenApiFactory.php | 45 +++++++++++ .../Controller/ApiPlatformController.php | 30 ++++++++ .../Compiler/ClassNameResourceNamePass.php | 42 ++++++++++ .../IbexaRestExtension.php | 1 + src/bundle/IbexaRestBundle.php | 1 + src/bundle/Resources/config/aaaaa.yaml | 31 ++++++++ src/bundle/Resources/config/api_platform.yml | 33 ++++++++ .../Resources/config/api_platform_routing.yml | 4 + src/lib/Server/Controller/Language.php | 76 +++++++++++++++++++ 10 files changed, 301 insertions(+) create mode 100644 src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php create mode 100644 src/bundle/ApiPlatform/OpenApiFactory.php create mode 100644 src/bundle/Controller/ApiPlatformController.php create mode 100644 src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php create mode 100644 src/bundle/Resources/config/aaaaa.yaml create mode 100644 src/bundle/Resources/config/api_platform.yml create mode 100644 src/bundle/Resources/config/api_platform_routing.yml diff --git a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php new file mode 100644 index 000000000..a53d242ba --- /dev/null +++ b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php @@ -0,0 +1,38 @@ + + */ + private array $resources = []; + + public function create(): ResourceNameCollection + { + return new ResourceNameCollection($this->resources); + // TODO: autotagging + } + + /** + * @param array $newResources + */ + public function addResources(array $newResources): void + { + $this->resources = array_merge($this->resources, $newResources); + } +} diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php new file mode 100644 index 000000000..86e3580da --- /dev/null +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -0,0 +1,45 @@ +decorated->__invoke($context); + + $schemas = new \ArrayObject(); + $schemas['Language'] = [ + 'type' => 'object', + 'properties' => [ + 'name' => [ + 'type' => 'string', + 'example' => 'Polish', + ], + 'code' => [ + 'type' => 'string', + 'example' => 'Pl-pl', + ], + ], + ]; + + $components = $openApi->getComponents(); + $components = $components->withSchemas($schemas); + +// $openApi = $openApi->withComponents($components); + + return $openApi; + } +} diff --git a/src/bundle/Controller/ApiPlatformController.php b/src/bundle/Controller/ApiPlatformController.php new file mode 100644 index 000000000..b318ccf7a --- /dev/null +++ b/src/bundle/Controller/ApiPlatformController.php @@ -0,0 +1,30 @@ +entrypointAction->__invoke($request); + } +} diff --git a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php new file mode 100644 index 000000000..9ceb4ad15 --- /dev/null +++ b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php @@ -0,0 +1,42 @@ +hasDefinition('ibexa.api_platform.metadata.resource.name_collection_factory')) { + return; + } + + $definition = $container->getDefinition('ibexa.api_platform.metadata.resource.name_collection_factory'); + + $definition->addMethodCall( + 'addResources', + [[ + 'Ibexa\\Rest\\Server\\Controller\\Language', + ]] + ); +// $taggedServiceIds = $container->findTaggedServiceIds(self::INPUT_HANDLER_SERVICE_TAG); +// foreach ($taggedServiceIds as $id => $attributes) { +// $definition->addMethodCall( +// 'addResources', +// [[ +// 'Ibexa\\Rest\\Server\\Controller\\Language', +// ]] +// ); +// } + } +} diff --git a/src/bundle/DependencyInjection/IbexaRestExtension.php b/src/bundle/DependencyInjection/IbexaRestExtension.php index 42f8df050..7db2e3a4b 100644 --- a/src/bundle/DependencyInjection/IbexaRestExtension.php +++ b/src/bundle/DependencyInjection/IbexaRestExtension.php @@ -40,6 +40,7 @@ public function load(array $configs, ContainerBuilder $container) $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yml'); + $loader->load('api_platform.yml'); $loader->load('value_object_visitors.yml'); $loader->load('input_parsers.yml'); $loader->load('security.yml'); diff --git a/src/bundle/IbexaRestBundle.php b/src/bundle/IbexaRestBundle.php index 0bdbb1d74..61dec475a 100644 --- a/src/bundle/IbexaRestBundle.php +++ b/src/bundle/IbexaRestBundle.php @@ -23,6 +23,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new Compiler\InputParserPass()); $container->addCompilerPass(new Compiler\OutputVisitorPass()); $container->addCompilerPass(new Compiler\ValueObjectVisitorPass()); + $container->addCompilerPass(new Compiler\ClassNameResourceNamePass()); if ($container->hasExtension('lexik_jwt_authentication')) { $container->addCompilerPass(new Compiler\LexikAuthorizationHeaderBridgePass()); diff --git a/src/bundle/Resources/config/aaaaa.yaml b/src/bundle/Resources/config/aaaaa.yaml new file mode 100644 index 000000000..d466cf5d4 --- /dev/null +++ b/src/bundle/Resources/config/aaaaa.yaml @@ -0,0 +1,31 @@ +api_platform.action.entrypoint: + Service(api_platform.metadata.resource.name_collection_factory.cached): (ApiPlatform\Metadata\Resource\Factory\CachedResourceNameCollectionFactory) + Service(api_platform.cache.metadata.resource): + Service(api_platform.metadata.resource.name_collection_factory.yaml): (ApiPlatform\Metadata\Resource\Factory\ExtractorResourceNameCollectionFactory) + Service(api_platform.metadata.resource_extractor.yaml): (ApiPlatform\Metadata\Extractor\YamlResourceExtractor) + Array (0 element(s)) + Service(service_container) + Service(api_platform.metadata.resource.name_collection_factory.class_name): + + Service(api_platform.state_provider.documentation.content_negotiation): (ApiPlatform\State\Provider\ContentNegotiationProvider) + Service(api_platform.swagger_ui.documentation.provider): (ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiProvider) + Service(api_platform.swagger_ui.documentation.provider.inner): + Service(api_platform.openapi.factory): + Service(api_platform.metadata.resource.name_collection_factory.cached): + Service(api_platform.metadata.resource.metadata_collection_factory.cached): + Service(api_platform.metadata.property.name_collection_factory.cached): + Service(api_platform.metadata.property.metadata_factory.cached): + Service(api_platform.json_schema.backward_compatible_schema_factory): + Service(api_platform.json_schema.type_factory): + Service(api_platform.filter_locator): + %api_platform.formats%: + Service(api_platform.openapi.options): + Service(api_platform.pagination_options): + Service(api_platform.router): + Service(api_platform.negotiator): + %api_platform.formats%: + %api_platform.error_formats%: + + Service(api_platform.state_processor.documentation.write): + + %api_platform.docs_formats%: diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml new file mode 100644 index 000000000..732eee0df --- /dev/null +++ b/src/bundle/Resources/config/api_platform.yml @@ -0,0 +1,33 @@ +services: + Ibexa\Bundle\Rest\Controller\ApiPlatformController: + parent: Ibexa\Contracts\AdminUi\Controller\Controller + arguments: + $entrypointAction: '@ibexa.api_platform.action.entrypoint' + + ibexa.api_platform.action.entrypoint: + parent: api_platform.action.entrypoint + arguments: + index_1: '@ibexa.api_platform.state_provider.documentation.content_negotiation' + + ibexa.api_platform.state_provider.documentation.content_negotiation: + parent: api_platform.state_provider.documentation.content_negotiation + arguments: + index_0: '@ibexa.api_platform.swagger_ui.documentation.provider' + + ibexa.api_platform.swagger_ui.documentation.provider: + parent: api_platform.swagger_ui.documentation.provider + arguments: + index_1: '@ibexa.api_platform.ibexa_openapi.factory' + + ibexa.api_platform.ibexa_openapi.factory: + class: 'Ibexa\Bundle\Rest\ApiPlatform\OpenApiFactory' + decorates: ibexa.api_platform.openapi.factory + arguments: ['@.inner'] + + ibexa.api_platform.openapi.factory: + parent: api_platform.openapi.factory + arguments: + index_0: '@ibexa.api_platform.metadata.resource.name_collection_factory' + + ibexa.api_platform.metadata.resource.name_collection_factory: + class: 'Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' diff --git a/src/bundle/Resources/config/api_platform_routing.yml b/src/bundle/Resources/config/api_platform_routing.yml new file mode 100644 index 000000000..cb9e19cf3 --- /dev/null +++ b/src/bundle/Resources/config/api_platform_routing.yml @@ -0,0 +1,4 @@ +ibexa.api_platform.documentation: + path: /api_doc + defaults: + _controller: 'Ibexa\Bundle\Rest\Controller\ApiPlatformController::documentationAction' diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index 9c005c800..ac486a9ac 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -8,12 +8,88 @@ namespace Ibexa\Rest\Server\Controller; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\OpenApi\Model; +use ApiPlatform\Metadata\Post; use Ibexa\Contracts\Core\Repository\LanguageService; use Ibexa\Contracts\Core\Repository\Values\Content\Language as ApiLanguage; use Ibexa\Rest\Server\Controller as RestController; use Ibexa\Rest\Server\Values\LanguageList; +use Symfony\Component\HttpFoundation\Response; use Traversable; +//#[Post(uriTemplate: '/books/{id}/publication', +//// formats: [ +//// 'csv' => ['text/html'], +//// ], +//// input: TestInputDto::class, +//// output: TestAnotherDto::class, +// name: 'name2B', +//)] +#[Post( + uriTemplate: '/content/locations/\{locationPath}', + openapi: new Model\Operation( + tags:[ + 'My-tag', +// 'Myyyyy-tttag' + ], + responses: [ + Response::HTTP_OK => [ + 'description' => 'My description', + 'content' => [ + 'application/vnd.ibexa.api.LocationCopyOutput' => [ + 'schema' => [ + 'type' => 'object', + 'properties' => [ + 'name' => ['type' => 'string'], + 'description' => ['type' => 'string'] + ] + ], + 'example' => [ + 'name' => 'Article A', + 'description' => 'Article A is an article' + ] + ], + ], + ], + Response::HTTP_CREATED => [], + ], + summary: 'Operations on locations', + description: 'Various operations on locations', + parameters: [ + new Model\Parameter( + name: 'locationPath', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + ], + requestBody: new Model\RequestBody( + content: new \ArrayObject([ + 'application/vnd.ibexa.api.LocationCopyInput' => [ + 'schema' => [ + '$ref' => "#/components/schemas/Language", + ], +// 'schema' => [ +// 'type' => 'object', +// 'properties' => [ +// 'namess' => ['type' => 'string'], +// 'descriptionss' => ['type' => 'string'] +// ] +// ], +// 'example' => [ +// 'name' => 'Article A', +// 'description' => 'Article A is an article' +// ] + ], + ]) + ) + ), + name: 'locations_operations', +)] final class Language extends RestController { private LanguageService $languageService; From 52a2cfcc66c07ddbedc992985566e61a1123202c Mon Sep 17 00:00:00 2001 From: tischsoic Date: Mon, 1 Jul 2024 14:47:18 +0200 Subject: [PATCH 04/23] improvements, cleanup etc --- src/bundle/ApiPlatform/OpenApiFactory.php | 2 +- .../Controller/ApiPlatformController.php | 8 ++--- .../Compiler/ClassNameResourceNamePass.php | 30 +++++++----------- src/bundle/Resources/config/aaaaa.yaml | 31 ------------------- src/bundle/Resources/config/api_platform.yml | 10 +++--- .../Resources/config/api_platform_routing.yml | 4 --- src/bundle/Resources/config/routing.yml | 10 ++++++ src/bundle/Resources/config/services.yml | 2 +- 8 files changed, 31 insertions(+), 66 deletions(-) delete mode 100644 src/bundle/Resources/config/aaaaa.yaml delete mode 100644 src/bundle/Resources/config/api_platform_routing.yml diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index 86e3580da..782c203c4 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -38,7 +38,7 @@ public function __invoke(array $context = []): OpenApi $components = $openApi->getComponents(); $components = $components->withSchemas($schemas); -// $openApi = $openApi->withComponents($components); + $openApi = $openApi->withComponents($components); return $openApi; } diff --git a/src/bundle/Controller/ApiPlatformController.php b/src/bundle/Controller/ApiPlatformController.php index b318ccf7a..4bd70dddf 100644 --- a/src/bundle/Controller/ApiPlatformController.php +++ b/src/bundle/Controller/ApiPlatformController.php @@ -8,18 +8,14 @@ namespace Ibexa\Bundle\Rest\Controller; use ApiPlatform\Symfony\Action\EntrypointAction; -use Ibexa\AdminUi\Form\Factory\FormFactory; -use Ibexa\Contracts\AdminUi\Controller\Controller; -use Ibexa\Contracts\Core\Repository\PermissionResolver; -use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -class ApiPlatformController extends Controller +class ApiPlatformController extends AbstractController { public function __construct( - private EntrypointAction $entrypointAction, + private readonly EntrypointAction $entrypointAction, ) { } diff --git a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php index 9ceb4ad15..04d7203e6 100644 --- a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php +++ b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php @@ -7,36 +7,30 @@ namespace Ibexa\Bundle\Rest\DependencyInjection\Compiler; +use Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; class ClassNameResourceNamePass implements CompilerPassInterface { - public const API_PLATFORM_CLASS_NAME_RESOURCE_SERVICE_TAG = 'ibexa.rest.api_platform.class_name_resource'; + public const API_PLATFORM_RESOURCE_SERVICE_TAG = 'ibexa.api_platform.resource'; public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('ibexa.api_platform.metadata.resource.name_collection_factory')) { + if (!$container->hasDefinition(ClassNameResourceNameCollectionFactory::class)) { return; } - $definition = $container->getDefinition('ibexa.api_platform.metadata.resource.name_collection_factory'); + $definition = $container->getDefinition(ClassNameResourceNameCollectionFactory::class); - $definition->addMethodCall( - 'addResources', - [[ - 'Ibexa\\Rest\\Server\\Controller\\Language', - ]] - ); -// $taggedServiceIds = $container->findTaggedServiceIds(self::INPUT_HANDLER_SERVICE_TAG); -// foreach ($taggedServiceIds as $id => $attributes) { -// $definition->addMethodCall( -// 'addResources', -// [[ -// 'Ibexa\\Rest\\Server\\Controller\\Language', -// ]] -// ); -// } + $taggedServiceIds = $container->findTaggedServiceIds(self::API_PLATFORM_RESOURCE_SERVICE_TAG); + foreach ($taggedServiceIds as $id => $attributes) { + $taggedServiceDefinition = $container->getDefinition($id); + $definition->addMethodCall( + 'addResources', + [[ $taggedServiceDefinition->getClass() ]] + ); + } } } diff --git a/src/bundle/Resources/config/aaaaa.yaml b/src/bundle/Resources/config/aaaaa.yaml deleted file mode 100644 index d466cf5d4..000000000 --- a/src/bundle/Resources/config/aaaaa.yaml +++ /dev/null @@ -1,31 +0,0 @@ -api_platform.action.entrypoint: - Service(api_platform.metadata.resource.name_collection_factory.cached): (ApiPlatform\Metadata\Resource\Factory\CachedResourceNameCollectionFactory) - Service(api_platform.cache.metadata.resource): - Service(api_platform.metadata.resource.name_collection_factory.yaml): (ApiPlatform\Metadata\Resource\Factory\ExtractorResourceNameCollectionFactory) - Service(api_platform.metadata.resource_extractor.yaml): (ApiPlatform\Metadata\Extractor\YamlResourceExtractor) - Array (0 element(s)) - Service(service_container) - Service(api_platform.metadata.resource.name_collection_factory.class_name): - - Service(api_platform.state_provider.documentation.content_negotiation): (ApiPlatform\State\Provider\ContentNegotiationProvider) - Service(api_platform.swagger_ui.documentation.provider): (ApiPlatform\Symfony\Bundle\SwaggerUi\SwaggerUiProvider) - Service(api_platform.swagger_ui.documentation.provider.inner): - Service(api_platform.openapi.factory): - Service(api_platform.metadata.resource.name_collection_factory.cached): - Service(api_platform.metadata.resource.metadata_collection_factory.cached): - Service(api_platform.metadata.property.name_collection_factory.cached): - Service(api_platform.metadata.property.metadata_factory.cached): - Service(api_platform.json_schema.backward_compatible_schema_factory): - Service(api_platform.json_schema.type_factory): - Service(api_platform.filter_locator): - %api_platform.formats%: - Service(api_platform.openapi.options): - Service(api_platform.pagination_options): - Service(api_platform.router): - Service(api_platform.negotiator): - %api_platform.formats%: - %api_platform.error_formats%: - - Service(api_platform.state_processor.documentation.write): - - %api_platform.docs_formats%: diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index 732eee0df..dd27fda7a 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -1,8 +1,9 @@ services: Ibexa\Bundle\Rest\Controller\ApiPlatformController: - parent: Ibexa\Contracts\AdminUi\Controller\Controller + autowire: true + autoconfigure: true arguments: - $entrypointAction: '@ibexa.api_platform.action.entrypoint' + - '@ibexa.api_platform.action.entrypoint' ibexa.api_platform.action.entrypoint: parent: api_platform.action.entrypoint @@ -27,7 +28,6 @@ services: ibexa.api_platform.openapi.factory: parent: api_platform.openapi.factory arguments: - index_0: '@ibexa.api_platform.metadata.resource.name_collection_factory' + index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' - ibexa.api_platform.metadata.resource.name_collection_factory: - class: 'Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' + Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory: ~ diff --git a/src/bundle/Resources/config/api_platform_routing.yml b/src/bundle/Resources/config/api_platform_routing.yml deleted file mode 100644 index cb9e19cf3..000000000 --- a/src/bundle/Resources/config/api_platform_routing.yml +++ /dev/null @@ -1,4 +0,0 @@ -ibexa.api_platform.documentation: - path: /api_doc - defaults: - _controller: 'Ibexa\Bundle\Rest\Controller\ApiPlatformController::documentationAction' diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index 69e858b8d..a8ae0b9d0 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -8,6 +8,16 @@ ibexa.rest.load_root_resource: methods: [GET] + +# OpenAPI documentation + + +ibexa.api_platform.documentation: + path: /doc + controller: Ibexa\Bundle\Rest\Controller\ApiPlatformController::documentationAction + + + # Sections diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index ca2f412e6..b3c55e76b 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -118,7 +118,7 @@ services: Ibexa\Rest\Server\Controller\Language: autowire: true - tags: [controller.service_arguments] + tags: [controller.service_arguments, ibexa.api_platform.resource] Ibexa\Rest\Server\Controller\Location: parent: Ibexa\Rest\Server\Controller From adf4b2b7b6f43bdb043b936df633f7452e777595 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Mon, 1 Jul 2024 15:31:12 +0200 Subject: [PATCH 05/23] autotagging - NOT working for every service --- src/bundle/DependencyInjection/IbexaRestExtension.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bundle/DependencyInjection/IbexaRestExtension.php b/src/bundle/DependencyInjection/IbexaRestExtension.php index 7db2e3a4b..1a7b2343e 100644 --- a/src/bundle/DependencyInjection/IbexaRestExtension.php +++ b/src/bundle/DependencyInjection/IbexaRestExtension.php @@ -8,6 +8,8 @@ namespace Ibexa\Bundle\Rest\DependencyInjection; use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; +use Ibexa\Bundle\Rest\DependencyInjection\Compiler\ClassNameResourceNamePass; +use Ibexa\Rest\Server\Controller as RestController; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -35,6 +37,8 @@ public function getAlias(): string */ public function load(array $configs, ContainerBuilder $container) { + $this->configureApiPlatformAutotagging($container); + $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); @@ -84,4 +88,10 @@ private function prependJMSTranslation(ContainerBuilder $container): void ], ]); } + + private function configureApiPlatformAutotagging(ContainerBuilder $container): void + { + $container->registerForAutoconfiguration(RestController::class) + ->addTag(ClassNameResourceNamePass::API_PLATFORM_RESOURCE_SERVICE_TAG); + } } From d8ad694e384b31e8461552a679fc70ffaea87aab Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 11:12:06 +0200 Subject: [PATCH 06/23] prepare language controller attributes --- src/bundle/ApiPlatform/OpenApiFactory.php | 45 ++++++- src/lib/Server/Controller/Language.php | 144 ++++++++++++++-------- 2 files changed, 134 insertions(+), 55 deletions(-) diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index 782c203c4..31fc0b019 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -21,19 +21,54 @@ public function __invoke(array $context = []): OpenApi $openApi = $this->decorated->__invoke($context); $schemas = new \ArrayObject(); - $schemas['Language'] = [ + $schemas['BaseObject'] = [ 'type' => 'object', + 'required' => [ '_media-type', '_href' ], 'properties' => [ - 'name' => [ + '_media-type' => [ 'type' => 'string', - 'example' => 'Polish', ], - 'code' => [ + '_href' => [ 'type' => 'string', - 'example' => 'Pl-pl', ], ], ]; + $schemas['Language'] = [ + 'allOf' => [ + [ + '$ref' => '#/components/schemas/BaseObject' + ], + [ + 'type' => 'object', + 'required' => [ 'id', 'languageCode', 'name', 'enabled' ], + 'properties' => [ + 'id' => [ + 'description' => 'The language ID (auto generated).', + 'type' => 'integer', + ], + 'languageCode' => [ + 'description' => 'The languageCode code.', + 'type' => 'string', + ], + 'name' => [ + 'description' => 'Human readable name of the language.', + 'type' => 'string', + ], + 'enabled' => [ + 'description' => 'Indicates if the language is enabled or not.', + 'type' => 'boolean' + ], + ], + ], + ], + ]; + $schemas['LanguageList'] = [ + 'description' => ' List of languages.', + 'type' => 'array', + 'items' => [ + '$ref' => '#/components/schemas/Language' + ], + ]; $components = $openApi->getComponents(); $components = $components->withSchemas($schemas); diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index ac486a9ac..87167b353 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -8,10 +8,8 @@ namespace Ibexa\Rest\Server\Controller; -use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; use ApiPlatform\OpenApi\Model; -use ApiPlatform\Metadata\Post; use Ibexa\Contracts\Core\Repository\LanguageService; use Ibexa\Contracts\Core\Repository\Values\Content\Language as ApiLanguage; use Ibexa\Rest\Server\Controller as RestController; @@ -19,47 +17,113 @@ use Symfony\Component\HttpFoundation\Response; use Traversable; -//#[Post(uriTemplate: '/books/{id}/publication', -//// formats: [ -//// 'csv' => ['text/html'], -//// ], -//// input: TestInputDto::class, -//// output: TestAnotherDto::class, -// name: 'name2B', -//)] -#[Post( - uriTemplate: '/content/locations/\{locationPath}', +#[Get( + uriTemplate: '/languages', openapi: new Model\Operation( - tags:[ - 'My-tag', -// 'Myyyyy-tttag' + tags: [ + 'Language attr on Controller', ], responses: [ Response::HTTP_OK => [ - 'description' => 'My description', 'content' => [ - 'application/vnd.ibexa.api.LocationCopyOutput' => [ + 'application/vnd.ibexa.api.LanguageList+xml' => [ 'schema' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'string'], - 'description' => ['type' => 'string'] - ] + '$ref' => "#/components/schemas/LanguageList", + ], + 'example' => << + + + 2 + eng-GB + English (United Kingdom) + + + 4 + pol-PL + Polish (polski) + + + XML + ], + 'application/vnd.ibexa.api.LanguageList+json' => [ + 'schema' => [ + '$ref' => "#/components/schemas/LanguageList", + ], + 'example' => [ + 'LanguageList' => [ + '_media-type' => 'application/vnd.ibexa.api.LanguageList+json', + '_href' => '/api/ibexa/v2/languages', + 'Language' => [ + [ + '_media-type' => 'application/vnd.ibexa.api.Language+json', + '_href' => '/api/ibexa/v2/languages/eng-GB', + 'languageId' => 2, + 'languageCode' => 'eng-GB', + 'name' => 'English (United Kingdom)', + ], + [ + '_media-type' => 'application/vnd.ibexa.api.Language+json', + '_href' => '/api/ibexa/v2/languages/pol-PL', + 'languageId' => 4, + 'languageCode' => 'pol-PL', + 'name' => 'Polish (polski)', + ], + ], + ], + ], + ], + ], + ], + ], + summary: 'Language list', + description: 'Lists languages', + ), + name: 'languages', +)] +#[Get( + uriTemplate: '/languages/{code}', + openapi: new Model\Operation( + tags: [ + 'Language attr on Controller', + ], + responses: [ + Response::HTTP_OK => [ + 'content' => [ + 'application/vnd.ibexa.api.Language+xml' => [ + 'schema' => [ + '$ref' => "#/components/schemas/Language", + ], + 'example' => << + + 2 + eng-GB + English (United Kingdom) + + XML + ], + 'application/vnd.ibexa.api.Language+json' => [ + 'schema' => [ + '$ref' => "#/components/schemas/Language", ], 'example' => [ - 'name' => 'Article A', - 'description' => 'Article A is an article' - ] + 'Language' => [ + '_media-type' => 'application/vnd.ibexa.api.Language+json', + '_href' => '/api/ibexa/v2/languages/eng-GB', + 'languageId' => '2', + 'languageCode' => 'eng-GB', + 'name' => 'English (United Kingdom)', + ] + ], ], ], ], - Response::HTTP_CREATED => [], ], - summary: 'Operations on locations', - description: 'Various operations on locations', + summary: 'Get language', parameters: [ new Model\Parameter( - name: 'locationPath', + name: 'code', in: 'path', required: true, schema: [ @@ -67,28 +131,8 @@ ], ), ], - requestBody: new Model\RequestBody( - content: new \ArrayObject([ - 'application/vnd.ibexa.api.LocationCopyInput' => [ - 'schema' => [ - '$ref' => "#/components/schemas/Language", - ], -// 'schema' => [ -// 'type' => 'object', -// 'properties' => [ -// 'namess' => ['type' => 'string'], -// 'descriptionss' => ['type' => 'string'] -// ] -// ], -// 'example' => [ -// 'name' => 'Article A', -// 'description' => 'Article A is an article' -// ] - ], - ]) - ) ), - name: 'locations_operations', + name: 'languages_code', )] final class Language extends RestController { From e9d9fdec0fcd7c9f51bdc8d6d7a369bf00fd43b7 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 11:49:14 +0200 Subject: [PATCH 07/23] Remove unnecessary controller --- .../Controller/ApiPlatformController.php | 26 ------------------- src/bundle/Resources/config/api_platform.yml | 6 ----- src/bundle/Resources/config/routing.yml | 2 +- 3 files changed, 1 insertion(+), 33 deletions(-) delete mode 100644 src/bundle/Controller/ApiPlatformController.php diff --git a/src/bundle/Controller/ApiPlatformController.php b/src/bundle/Controller/ApiPlatformController.php deleted file mode 100644 index 4bd70dddf..000000000 --- a/src/bundle/Controller/ApiPlatformController.php +++ /dev/null @@ -1,26 +0,0 @@ -entrypointAction->__invoke($request); - } -} diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index dd27fda7a..e8cca6778 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -1,10 +1,4 @@ services: - Ibexa\Bundle\Rest\Controller\ApiPlatformController: - autowire: true - autoconfigure: true - arguments: - - '@ibexa.api_platform.action.entrypoint' - ibexa.api_platform.action.entrypoint: parent: api_platform.action.entrypoint arguments: diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index a8ae0b9d0..9a68f5dd8 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -14,7 +14,7 @@ ibexa.rest.load_root_resource: ibexa.api_platform.documentation: path: /doc - controller: Ibexa\Bundle\Rest\Controller\ApiPlatformController::documentationAction + controller: ibexa.api_platform.action.entrypoint From d8473d12d3741a30271340cb974c2ba2245e9f94 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 13:45:59 +0200 Subject: [PATCH 08/23] Remove unnecessary comment --- .../ApiPlatform/ClassNameResourceNameCollectionFactory.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php index a53d242ba..c0f6284a7 100644 --- a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php +++ b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php @@ -25,7 +25,6 @@ final class ClassNameResourceNameCollectionFactory implements ResourceNameCollec public function create(): ResourceNameCollection { return new ResourceNameCollection($this->resources); - // TODO: autotagging } /** From 6b56a98a527b5f74e8e3c23d5059a3e38bb90d5c Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 13:51:06 +0200 Subject: [PATCH 09/23] fix cs --- .../ClassNameResourceNameCollectionFactory.php | 1 - src/bundle/ApiPlatform/OpenApiFactory.php | 15 ++++++++------- .../Compiler/ClassNameResourceNamePass.php | 3 +-- src/lib/Server/Controller/Language.php | 10 +++++----- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php index c0f6284a7..e3123c632 100644 --- a/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php +++ b/src/bundle/ApiPlatform/ClassNameResourceNameCollectionFactory.php @@ -4,7 +4,6 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ - declare(strict_types=1); namespace Ibexa\Bundle\Rest\ApiPlatform; diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index 31fc0b019..a2aa097a4 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -4,7 +4,6 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ - declare(strict_types=1); namespace Ibexa\Bundle\Rest\ApiPlatform; @@ -14,7 +13,9 @@ final class OpenApiFactory implements OpenApiFactoryInterface { - public function __construct(private readonly OpenApiFactoryInterface $decorated) { } + public function __construct(private readonly OpenApiFactoryInterface $decorated) + { + } public function __invoke(array $context = []): OpenApi { @@ -23,7 +24,7 @@ public function __invoke(array $context = []): OpenApi $schemas = new \ArrayObject(); $schemas['BaseObject'] = [ 'type' => 'object', - 'required' => [ '_media-type', '_href' ], + 'required' => ['_media-type', '_href'], 'properties' => [ '_media-type' => [ 'type' => 'string', @@ -36,11 +37,11 @@ public function __invoke(array $context = []): OpenApi $schemas['Language'] = [ 'allOf' => [ [ - '$ref' => '#/components/schemas/BaseObject' + '$ref' => '#/components/schemas/BaseObject', ], [ 'type' => 'object', - 'required' => [ 'id', 'languageCode', 'name', 'enabled' ], + 'required' => ['id', 'languageCode', 'name', 'enabled'], 'properties' => [ 'id' => [ 'description' => 'The language ID (auto generated).', @@ -56,7 +57,7 @@ public function __invoke(array $context = []): OpenApi ], 'enabled' => [ 'description' => 'Indicates if the language is enabled or not.', - 'type' => 'boolean' + 'type' => 'boolean', ], ], ], @@ -66,7 +67,7 @@ public function __invoke(array $context = []): OpenApi 'description' => ' List of languages.', 'type' => 'array', 'items' => [ - '$ref' => '#/components/schemas/Language' + '$ref' => '#/components/schemas/Language', ], ]; diff --git a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php index 04d7203e6..e47f49f76 100644 --- a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php +++ b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php @@ -10,7 +10,6 @@ use Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; class ClassNameResourceNamePass implements CompilerPassInterface { @@ -29,7 +28,7 @@ public function process(ContainerBuilder $container) $taggedServiceDefinition = $container->getDefinition($id); $definition->addMethodCall( 'addResources', - [[ $taggedServiceDefinition->getClass() ]] + [[$taggedServiceDefinition->getClass()]] ); } } diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index 87167b353..105402faf 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -28,7 +28,7 @@ 'content' => [ 'application/vnd.ibexa.api.LanguageList+xml' => [ 'schema' => [ - '$ref' => "#/components/schemas/LanguageList", + '$ref' => '#/components/schemas/LanguageList', ], 'example' => << @@ -48,7 +48,7 @@ ], 'application/vnd.ibexa.api.LanguageList+json' => [ 'schema' => [ - '$ref' => "#/components/schemas/LanguageList", + '$ref' => '#/components/schemas/LanguageList', ], 'example' => [ 'LanguageList' => [ @@ -92,7 +92,7 @@ 'content' => [ 'application/vnd.ibexa.api.Language+xml' => [ 'schema' => [ - '$ref' => "#/components/schemas/Language", + '$ref' => '#/components/schemas/Language', ], 'example' => << @@ -105,7 +105,7 @@ ], 'application/vnd.ibexa.api.Language+json' => [ 'schema' => [ - '$ref' => "#/components/schemas/Language", + '$ref' => '#/components/schemas/Language', ], 'example' => [ 'Language' => [ @@ -114,7 +114,7 @@ 'languageId' => '2', 'languageCode' => 'eng-GB', 'name' => 'English (United Kingdom)', - ] + ], ], ], ], From cdc8f37cb4f18e3116d863aef775b0adfb078a8c Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 13:55:28 +0200 Subject: [PATCH 10/23] phpstan --- src/bundle/ApiPlatform/OpenApiFactory.php | 4 ++++ .../Compiler/ClassNameResourceNamePass.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index a2aa097a4..f61651408 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -17,10 +17,14 @@ public function __construct(private readonly OpenApiFactoryInterface $decorated) { } + /** + * @param array $context + */ public function __invoke(array $context = []): OpenApi { $openApi = $this->decorated->__invoke($context); + /** @var \ArrayObject $schemas */ $schemas = new \ArrayObject(); $schemas['BaseObject'] = [ 'type' => 'object', diff --git a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php index e47f49f76..c37caa4f2 100644 --- a/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php +++ b/src/bundle/DependencyInjection/Compiler/ClassNameResourceNamePass.php @@ -15,7 +15,7 @@ class ClassNameResourceNamePass implements CompilerPassInterface { public const API_PLATFORM_RESOURCE_SERVICE_TAG = 'ibexa.api_platform.resource'; - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { if (!$container->hasDefinition(ClassNameResourceNameCollectionFactory::class)) { return; From dfda25b70395d4f44e66c1207c30fdf704751387 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 14:57:30 +0200 Subject: [PATCH 11/23] revert original php version requirement --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6afefaec1..2b05ee72e 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } }, "require": { - "php": " ^8.1", + "php": ">=8.3", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", From 723516bf4760f2f102529da12267030bba915e92 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 2 Jul 2024 14:58:06 +0200 Subject: [PATCH 12/23] revert space character delete --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2b05ee72e..d09b56689 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ } }, "require": { - "php": ">=8.3", + "php": " >=8.3", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", From 600717f626b273b11afbab3e1cdbe3240c508365 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Wed, 3 Jul 2024 09:47:07 +0200 Subject: [PATCH 13/23] vcs --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index d09b56689..0f33fde9c 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "ext-libxml": "*", "ext-simplexml": "*", "ext-xmlwriter": "*", - "api-platform/core": "3.3.x-dev", + "api-platform/core": "dev-downgraded-deps", "hautelook/templated-uri-bundle": "^3.4", "ibexa/core": "~5.0.x-dev", "lexik/jwt-authentication-bundle": "^2.8", @@ -85,8 +85,8 @@ }, "repositories": { "api-platform/core": { - "type": "path", - "url": "~/Sites/openapi-forks/core" + "type": "vcs", + "url": "https://github.com/tischsoic/core" } }, "minimum-stability": "dev" From 998a6875e533ab1adc0a8a2d781108503d4e671f Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 4 Jul 2024 10:02:06 +0200 Subject: [PATCH 14/23] fix doc route --- src/bundle/Resources/config/api_platform.yml | 1 + src/bundle/Resources/config/routing.yml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index e8cca6778..340841f4b 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -2,6 +2,7 @@ services: ibexa.api_platform.action.entrypoint: parent: api_platform.action.entrypoint arguments: + index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' index_1: '@ibexa.api_platform.state_provider.documentation.content_negotiation' ibexa.api_platform.state_provider.documentation.content_negotiation: diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index 9a68f5dd8..c21eaca39 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -13,8 +13,11 @@ ibexa.rest.load_root_resource: ibexa.api_platform.documentation: - path: /doc + path: /doc/{index}.{_format} controller: ibexa.api_platform.action.entrypoint + defaults: + index: index + _format: ~ From ec3b0d59acfaf37159b983fc45c0239cecce2457 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Wed, 17 Jul 2024 16:29:00 +0200 Subject: [PATCH 15/23] Read schemas from yaml files --- src/bundle/ApiPlatform/OpenApiFactory.php | 60 +++---------------- .../ApiPlatform/SchemasCollectionFactory.php | 40 +++++++++++++ .../Compiler/SchemaProviderPass.php | 35 +++++++++++ .../IbexaRestExtension.php | 5 ++ src/bundle/IbexaRestBundle.php | 1 + src/bundle/Resources/api_platform/base.yml | 11 ++++ .../api_platform/language_schemas.yml | 29 +++++++++ src/bundle/Resources/config/api_platform.yml | 19 +++++- src/lib/ApiPlatform/SchemasCollection.php | 32 ++++++++++ .../SchemasCollectionFactoryInterface.php | 14 +++++ src/lib/ApiPlatform/SchemasProvider.php | 38 ++++++++++++ .../ApiPlatform/SchemasProviderInterface.php | 17 ++++++ 12 files changed, 247 insertions(+), 54 deletions(-) create mode 100644 src/bundle/ApiPlatform/SchemasCollectionFactory.php create mode 100644 src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php create mode 100644 src/bundle/Resources/api_platform/base.yml create mode 100644 src/bundle/Resources/api_platform/language_schemas.yml create mode 100644 src/lib/ApiPlatform/SchemasCollection.php create mode 100644 src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php create mode 100644 src/lib/ApiPlatform/SchemasProvider.php create mode 100644 src/lib/ApiPlatform/SchemasProviderInterface.php diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index f61651408..c9388d699 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -13,8 +13,10 @@ final class OpenApiFactory implements OpenApiFactoryInterface { - public function __construct(private readonly OpenApiFactoryInterface $decorated) - { + public function __construct( + private readonly OpenApiFactoryInterface $decorated, + private readonly SchemasCollectionFactory $schemaCollectionFactory, + ) { } /** @@ -24,59 +26,11 @@ public function __invoke(array $context = []): OpenApi { $openApi = $this->decorated->__invoke($context); - /** @var \ArrayObject $schemas */ - $schemas = new \ArrayObject(); - $schemas['BaseObject'] = [ - 'type' => 'object', - 'required' => ['_media-type', '_href'], - 'properties' => [ - '_media-type' => [ - 'type' => 'string', - ], - '_href' => [ - 'type' => 'string', - ], - ], - ]; - $schemas['Language'] = [ - 'allOf' => [ - [ - '$ref' => '#/components/schemas/BaseObject', - ], - [ - 'type' => 'object', - 'required' => ['id', 'languageCode', 'name', 'enabled'], - 'properties' => [ - 'id' => [ - 'description' => 'The language ID (auto generated).', - 'type' => 'integer', - ], - 'languageCode' => [ - 'description' => 'The languageCode code.', - 'type' => 'string', - ], - 'name' => [ - 'description' => 'Human readable name of the language.', - 'type' => 'string', - ], - 'enabled' => [ - 'description' => 'Indicates if the language is enabled or not.', - 'type' => 'boolean', - ], - ], - ], - ], - ]; - $schemas['LanguageList'] = [ - 'description' => ' List of languages.', - 'type' => 'array', - 'items' => [ - '$ref' => '#/components/schemas/Language', - ], - ]; + $schemasCollection = $this->schemaCollectionFactory->create(); + $schemas = iterator_to_array($schemasCollection); $components = $openApi->getComponents(); - $components = $components->withSchemas($schemas); + $components = $components->withSchemas(new \ArrayObject($schemas)); $openApi = $openApi->withComponents($components); diff --git a/src/bundle/ApiPlatform/SchemasCollectionFactory.php b/src/bundle/ApiPlatform/SchemasCollectionFactory.php new file mode 100644 index 000000000..09f64d607 --- /dev/null +++ b/src/bundle/ApiPlatform/SchemasCollectionFactory.php @@ -0,0 +1,40 @@ + + */ + private array $providers = []; + + public function create(): SchemasCollection + { + $schemas = []; + + foreach ($this->providers as $provider) { + $schemas = array_merge($schemas, $provider->getSchemas()); + } + + return new SchemasCollection($schemas); + } + + public function addProvider(SchemasProviderInterface $provider): void + { + $this->providers[] = $provider; + } +} diff --git a/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php b/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php new file mode 100644 index 000000000..f78cc6c68 --- /dev/null +++ b/src/bundle/DependencyInjection/Compiler/SchemaProviderPass.php @@ -0,0 +1,35 @@ +hasDefinition(SchemasCollectionFactory::class)) { + return; + } + + $definition = $container->getDefinition(SchemasCollectionFactory::class); + + $taggedServiceIds = $container->findTaggedServiceIds(self::API_PLATFORM_SCHEMA_PROVIDER_SERVICE_TAG); + foreach ($taggedServiceIds as $serviceId => $attributes) { + $definition->addMethodCall( + 'addProvider', + [new Reference($serviceId)] + ); + } + } +} diff --git a/src/bundle/DependencyInjection/IbexaRestExtension.php b/src/bundle/DependencyInjection/IbexaRestExtension.php index 1a7b2343e..dcd076ac3 100644 --- a/src/bundle/DependencyInjection/IbexaRestExtension.php +++ b/src/bundle/DependencyInjection/IbexaRestExtension.php @@ -9,6 +9,8 @@ use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; use Ibexa\Bundle\Rest\DependencyInjection\Compiler\ClassNameResourceNamePass; +use Ibexa\Bundle\Rest\DependencyInjection\Compiler\SchemaProviderPass; +use Ibexa\Rest\ApiPlatform\SchemasProviderInterface; use Ibexa\Rest\Server\Controller as RestController; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; @@ -93,5 +95,8 @@ private function configureApiPlatformAutotagging(ContainerBuilder $container): v { $container->registerForAutoconfiguration(RestController::class) ->addTag(ClassNameResourceNamePass::API_PLATFORM_RESOURCE_SERVICE_TAG); + + $container->registerForAutoconfiguration(SchemasProviderInterface::class) + ->addTag(SchemaProviderPass::API_PLATFORM_SCHEMA_PROVIDER_SERVICE_TAG); } } diff --git a/src/bundle/IbexaRestBundle.php b/src/bundle/IbexaRestBundle.php index 61dec475a..1c00c6338 100644 --- a/src/bundle/IbexaRestBundle.php +++ b/src/bundle/IbexaRestBundle.php @@ -24,6 +24,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new Compiler\OutputVisitorPass()); $container->addCompilerPass(new Compiler\ValueObjectVisitorPass()); $container->addCompilerPass(new Compiler\ClassNameResourceNamePass()); + $container->addCompilerPass(new Compiler\SchemaProviderPass()); if ($container->hasExtension('lexik_jwt_authentication')) { $container->addCompilerPass(new Compiler\LexikAuthorizationHeaderBridgePass()); diff --git a/src/bundle/Resources/api_platform/base.yml b/src/bundle/Resources/api_platform/base.yml new file mode 100644 index 000000000..2d2823ec1 --- /dev/null +++ b/src/bundle/Resources/api_platform/base.yml @@ -0,0 +1,11 @@ +schemas: + BaseObject: + type: object + required: + - _media-type + - _href + properties: + _media-type: + type: string + _href: + type: string diff --git a/src/bundle/Resources/api_platform/language_schemas.yml b/src/bundle/Resources/api_platform/language_schemas.yml new file mode 100644 index 000000000..02ae71b75 --- /dev/null +++ b/src/bundle/Resources/api_platform/language_schemas.yml @@ -0,0 +1,29 @@ +schemas: + Language: + allOf: + - ref: "#/components/schemas/BaseObject" + - type: object + description: This class represents a language in the Repository. + required: + - id + - languageCode + - name + - enabled + properties: + id: + description: The language ID (auto generated). + type: integer + languageCode: + description: The languageCode code. + type: string + name: + description: Human readable name of the language. + type: string + enabled: + description: Indicates if the language is enabled or not. + type: boolean + LanguageList: + description: List of languages. + type: array + items: + $ref: "#/components/schemas/Language" diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index 340841f4b..291b33ee9 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -18,7 +18,9 @@ services: ibexa.api_platform.ibexa_openapi.factory: class: 'Ibexa\Bundle\Rest\ApiPlatform\OpenApiFactory' decorates: ibexa.api_platform.openapi.factory - arguments: ['@.inner'] + arguments: + - '@.inner' + - '@Ibexa\Bundle\Rest\ApiPlatform\SchemasCollectionFactory' ibexa.api_platform.openapi.factory: parent: api_platform.openapi.factory @@ -26,3 +28,18 @@ services: index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory: ~ + + + # Collecting schemas + + Ibexa\Bundle\Rest\ApiPlatform\SchemasCollectionFactory: ~ + + ibexa.api_platform.schemas_provider.rest: + class: Ibexa\Rest\ApiPlatform\SchemasProvider + autowire: true + autoconfigure: true + arguments: + $files: + - '@@IbexaRestBundle/Resources/api_platform/base.yml' + - '@@IbexaRestBundle/Resources/api_platform/language_schemas.yml' + diff --git a/src/lib/ApiPlatform/SchemasCollection.php b/src/lib/ApiPlatform/SchemasCollection.php new file mode 100644 index 000000000..9eba9677c --- /dev/null +++ b/src/lib/ApiPlatform/SchemasCollection.php @@ -0,0 +1,32 @@ + $schemas + */ + public function __construct(private readonly array $schemas = []) + { + } + + /** + * @return \Traversable + */ + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->schemas); + } + + public function count(): int + { + return \count($this->schemas); + } +} diff --git a/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php b/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php new file mode 100644 index 000000000..0acc62a1a --- /dev/null +++ b/src/lib/ApiPlatform/SchemasCollectionFactoryInterface.php @@ -0,0 +1,14 @@ + $files + */ + public function __construct( + private readonly KernelInterface $kernel, + private readonly array $files, + ) { + } + + public function getSchemas(): array + { + $allSchemas = []; + + foreach ($this->files as $fileName) { + $filePath = $this->kernel->locateResource($fileName); + $schemas = Yaml::parseFile($filePath); + + $allSchemas = array_merge($allSchemas, $schemas['schemas']); + } + + return $allSchemas; + } +} diff --git a/src/lib/ApiPlatform/SchemasProviderInterface.php b/src/lib/ApiPlatform/SchemasProviderInterface.php new file mode 100644 index 000000000..b58bb8aeb --- /dev/null +++ b/src/lib/ApiPlatform/SchemasProviderInterface.php @@ -0,0 +1,17 @@ + + */ + public function getSchemas(): array; +} From dd6c9d0411dfdf5f92e3890003dce4c98ec9bc1b Mon Sep 17 00:00:00 2001 From: tischsoic Date: Fri, 19 Jul 2024 11:12:45 +0200 Subject: [PATCH 16/23] ci workflow --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c1f82d73a..4f4fda4e7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,6 +25,7 @@ jobs: - uses: ramsey/composer-install@v3 with: dependency-versions: highest + composer-options: "--ignore-platform-reqs --optimize-autoloader" - name: Run code style check run: composer run-script check-cs -- --format=checkstyle | cs2pr From ebff100191fb4284b29902ae4ab584a482833946 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 13 Aug 2024 15:10:48 +0200 Subject: [PATCH 17/23] Adjust language openapi data --- src/bundle/Resources/api_platform/base.yml | 5 +- .../api_platform/language_schemas.yml | 41 ++++++++---- src/bundle/Resources/config/api_platform.yml | 1 - src/lib/Server/Controller/Language.php | 67 ++++++++++++++----- 4 files changed, 82 insertions(+), 32 deletions(-) diff --git a/src/bundle/Resources/api_platform/base.yml b/src/bundle/Resources/api_platform/base.yml index 2d2823ec1..6ba853778 100644 --- a/src/bundle/Resources/api_platform/base.yml +++ b/src/bundle/Resources/api_platform/base.yml @@ -3,9 +3,12 @@ schemas: type: object required: - _media-type - - _href properties: _media-type: + xml: + attribute: true type: string _href: + xml: + attribute: true type: string diff --git a/src/bundle/Resources/api_platform/language_schemas.yml b/src/bundle/Resources/api_platform/language_schemas.yml index 02ae71b75..165e6afd2 100644 --- a/src/bundle/Resources/api_platform/language_schemas.yml +++ b/src/bundle/Resources/api_platform/language_schemas.yml @@ -2,15 +2,14 @@ schemas: Language: allOf: - ref: "#/components/schemas/BaseObject" - - type: object - description: This class represents a language in the Repository. + - description: This class represents a language in the Repository. + type: object required: - - id + - languageId - languageCode - name - - enabled properties: - id: + languageId: description: The language ID (auto generated). type: integer languageCode: @@ -19,11 +18,29 @@ schemas: name: description: Human readable name of the language. type: string - enabled: - description: Indicates if the language is enabled or not. - type: boolean + LanguageWrapper: + type: object + required: + - Language + properties: + Language: + $ref: "#/components/schemas/Language" LanguageList: - description: List of languages. - type: array - items: - $ref: "#/components/schemas/Language" + allOf: + - ref: "#/components/schemas/BaseObject" + - description: List of languages. + type: object + required: + - Language + properties: + Language: + type: array + items: + $ref: "#/components/schemas/Language" + LanguageListWrapper: + type: object + required: + - LanguageList + properties: + LanguageList: + $ref: "#/components/schemas/LanguageList" diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index 291b33ee9..eacda1071 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -42,4 +42,3 @@ services: $files: - '@@IbexaRestBundle/Resources/api_platform/base.yml' - '@@IbexaRestBundle/Resources/api_platform/language_schemas.yml' - diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index 105402faf..8c8ad500e 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -20,9 +20,27 @@ #[Get( uriTemplate: '/languages', openapi: new Model\Operation( + extensionProperties: [ + 'x-visib' => '555' + ], tags: [ 'Language attr on Controller', ], + parameters: [ + new Model\Parameter( + name: 'Accept', + description: 'If set, the list is returned in XML or JSON format.', + required: true, + in: 'header', + schema: [ + 'description' => 'If set, the list is returned in XML or JSON format.', + 'example' => 'application/vnd.ibexa.api.LanguageList+xml +application/vnd.ibexa.api.LanguageList+json +', + 'type' => 'string', + ], + ), + ], responses: [ Response::HTTP_OK => [ 'content' => [ @@ -48,7 +66,7 @@ ], 'application/vnd.ibexa.api.LanguageList+json' => [ 'schema' => [ - '$ref' => '#/components/schemas/LanguageList', + '$ref' => '#/components/schemas/LanguageListWrapper', ], 'example' => [ 'LanguageList' => [ @@ -87,6 +105,29 @@ tags: [ 'Language attr on Controller', ], + parameters: [ + new Model\Parameter( + name: 'code', + in: 'path', + required: true, + schema: [ + 'type' => 'string', + ], + ), + new Model\Parameter( + name: 'Accept', + description: 'If set, the language is returned in XML or JSON format.', + required: true, + in: 'header', + schema: [ + 'description' => 'If set, the language is returned in XML or JSON format.', + 'example' => 'application/vnd.ibexa.api.Language+xml +application/vnd.ibexa.api.Language+json +', + 'type' => 'string', + ], + ), + ], responses: [ Response::HTTP_OK => [ 'content' => [ @@ -95,17 +136,17 @@ '$ref' => '#/components/schemas/Language', ], 'example' => << - - 2 - eng-GB - English (United Kingdom) - + + + 2 + eng-GB + English (United Kingdom) + XML ], 'application/vnd.ibexa.api.Language+json' => [ 'schema' => [ - '$ref' => '#/components/schemas/Language', + '$ref' => '#/components/schemas/LanguageWrapper', ], 'example' => [ 'Language' => [ @@ -121,16 +162,6 @@ ], ], summary: 'Get language', - parameters: [ - new Model\Parameter( - name: 'code', - in: 'path', - required: true, - schema: [ - 'type' => 'string', - ], - ), - ], ), name: 'languages_code', )] From f45837ed03f2e124e4b7a842ddd98e74ba6cf34f Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 13 Aug 2024 15:11:08 +0200 Subject: [PATCH 18/23] fix SchemasProvider --- src/lib/ApiPlatform/SchemasProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/ApiPlatform/SchemasProvider.php b/src/lib/ApiPlatform/SchemasProvider.php index bd4d46e0f..c17e023c7 100644 --- a/src/lib/ApiPlatform/SchemasProvider.php +++ b/src/lib/ApiPlatform/SchemasProvider.php @@ -30,7 +30,9 @@ public function getSchemas(): array $filePath = $this->kernel->locateResource($fileName); $schemas = Yaml::parseFile($filePath); - $allSchemas = array_merge($allSchemas, $schemas['schemas']); + if (isset($schemas['schemas'])) { + $allSchemas = array_merge($allSchemas, $schemas['schemas']); + } } return $allSchemas; From 40cff86900b74aa3ef35ea37069465077499d894 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 13 Aug 2024 15:11:38 +0200 Subject: [PATCH 19/23] Add ApiPlatformBundle to IbexaTestKernel --- tests/integration/IbexaTestKernel.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration/IbexaTestKernel.php b/tests/integration/IbexaTestKernel.php index 6705424f6..888f98d56 100644 --- a/tests/integration/IbexaTestKernel.php +++ b/tests/integration/IbexaTestKernel.php @@ -8,6 +8,7 @@ namespace Ibexa\Tests\Integration\Rest; +use ApiPlatform\Symfony\Bundle\ApiPlatformBundle; use Hautelook\TemplatedUriBundle\HautelookTemplatedUriBundle; use Ibexa\Bundle\Rest\IbexaRestBundle; use Ibexa\Contracts\Rest\UriParser\UriParserInterface; @@ -25,6 +26,7 @@ public function registerBundles(): iterable yield new HautelookTemplatedUriBundle(); yield new IbexaRestBundle(); + yield new ApiPlatformBundle(); } public function registerContainerConfiguration(LoaderInterface $loader): void From dab22d3afa77e50f8503fcfb3d23aaddda0ddaec Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 27 Aug 2024 14:06:27 +0200 Subject: [PATCH 20/23] adjust schemas and annotations --- src/bundle/Resources/api_platform/base.yml | 14 -- .../Resources/api_platform/base_schemas.yml | 136 +++++++++++++++ .../api_platform/language_schemas.yml | 6 +- src/bundle/Resources/config/api_platform.yml | 2 +- src/lib/Server/Controller/Language.php | 160 +++++++++--------- 5 files changed, 219 insertions(+), 99 deletions(-) delete mode 100644 src/bundle/Resources/api_platform/base.yml create mode 100644 src/bundle/Resources/api_platform/base_schemas.yml diff --git a/src/bundle/Resources/api_platform/base.yml b/src/bundle/Resources/api_platform/base.yml deleted file mode 100644 index 6ba853778..000000000 --- a/src/bundle/Resources/api_platform/base.yml +++ /dev/null @@ -1,14 +0,0 @@ -schemas: - BaseObject: - type: object - required: - - _media-type - properties: - _media-type: - xml: - attribute: true - type: string - _href: - xml: - attribute: true - type: string diff --git a/src/bundle/Resources/api_platform/base_schemas.yml b/src/bundle/Resources/api_platform/base_schemas.yml new file mode 100644 index 000000000..67b6733f6 --- /dev/null +++ b/src/bundle/Resources/api_platform/base_schemas.yml @@ -0,0 +1,136 @@ +schemas: + BaseObject: + type: object + required: + - _media-type + properties: + _media-type: + xml: + attribute: true + name: media-type + type: string + _href: + xml: + attribute: true + name: href + type: string + Ref: + type: + $ref: "#/components/schemas/BaseObject" + UnixTimestamp: + type: integer + Href: + type: object + required: + - _href + properties: + _href: + xml: + attribute: true + name: href + type: string + Target: + description: Struct that stores extra target information for a SortClause object. + type: object + SortClause: + description: This class is the base for SortClause classes, used to set sorting of content queries. + type: object + required: + - direction + - target + - targetData + properties: + direction: + description: Sort direction. One of Query::SORT_ASC or Query::SORT_DESC. + type: string + target: + description: "Sort target, high level: section_identifier, attribute_value, etc." + type: string + targetData: + description: Extra target data, required by some sort clauses, field for instance. + type: + $ref: "#/components/schemas/Target" + ErrorMessage: + description: Represents an error response. Might contain additional properties depending on an error type. + type: object + required: + - errorCode + - errorMessage + - errorDescription + properties: + errorCode: + type: integer + errorMessage: + type: string + errorDescription: + type: string + Value: + description: Struct that stores extra value information for a Criterion object. + type: object + required: + - _languageCode + - "#text" + properties: + _languageCode: + description: Language code. + type: string + "#text": + description: Content type description. + type: [string, 'null'] + ValueObject: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + ValueArray: + type: object + required: + - value + properties: + value: + type: array + items: + $ref: "#/components/schemas/Value" + MultilingualValue: + allOf: + - $ref: "#/components/schemas/Value" + - description: Object that represents a multilingual (translated) value. + type: object + required: + - _languageCode + - "#text" + properties: + _languageCode: + description: Language code. + type: string + "#text": + description: Translation contents. + type: [string, 'null'] + KeyValue: + description: Key-value structure + type: object + required: + - _key + - "#text" + properties: + _key: + type: string + "#text": + type: [string, 'null'] + DateRange: + allOf: + - $ref: "#/components/schemas/BaseObject" + - description: Representation of date range. + type: object + required: + - startDate + - endDate + properties: + startDate: + type: string + endDate: + type: string diff --git a/src/bundle/Resources/api_platform/language_schemas.yml b/src/bundle/Resources/api_platform/language_schemas.yml index 165e6afd2..5750c417e 100644 --- a/src/bundle/Resources/api_platform/language_schemas.yml +++ b/src/bundle/Resources/api_platform/language_schemas.yml @@ -1,7 +1,7 @@ schemas: Language: allOf: - - ref: "#/components/schemas/BaseObject" + - $ref: "#/components/schemas/BaseObject" - description: This class represents a language in the Repository. type: object required: @@ -11,7 +11,7 @@ schemas: properties: languageId: description: The language ID (auto generated). - type: integer + type: [string, 'null'] languageCode: description: The languageCode code. type: string @@ -27,7 +27,7 @@ schemas: $ref: "#/components/schemas/Language" LanguageList: allOf: - - ref: "#/components/schemas/BaseObject" + - $ref: "#/components/schemas/BaseObject" - description: List of languages. type: object required: diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index eacda1071..a0f4f75d6 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -40,5 +40,5 @@ services: autoconfigure: true arguments: $files: - - '@@IbexaRestBundle/Resources/api_platform/base.yml' + - '@@IbexaRestBundle/Resources/api_platform/base_schemas.yml' - '@@IbexaRestBundle/Resources/api_platform/language_schemas.yml' diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index 8c8ad500e..c8d5ba23f 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -19,24 +19,19 @@ #[Get( uriTemplate: '/languages', + name: 'Language list', openapi: new Model\Operation( - extensionProperties: [ - 'x-visib' => '555' - ], + summary: 'Lists languages', tags: [ - 'Language attr on Controller', + 'Language', ], parameters: [ new Model\Parameter( name: 'Accept', - description: 'If set, the list is returned in XML or JSON format.', - required: true, in: 'header', + required: 'true', + description: 'If set, the list is returned in XML or JSON format.', schema: [ - 'description' => 'If set, the list is returned in XML or JSON format.', - 'example' => 'application/vnd.ibexa.api.LanguageList+xml -application/vnd.ibexa.api.LanguageList+json -', 'type' => 'string', ], ), @@ -48,82 +43,41 @@ 'schema' => [ '$ref' => '#/components/schemas/LanguageList', ], - 'example' => << - - - 2 - eng-GB - English (United Kingdom) - - - 4 - pol-PL - Polish (polski) - - - XML + 'example' => LANGUAGE_LIST_XML_EXAMPLE, ], 'application/vnd.ibexa.api.LanguageList+json' => [ 'schema' => [ '$ref' => '#/components/schemas/LanguageListWrapper', ], - 'example' => [ - 'LanguageList' => [ - '_media-type' => 'application/vnd.ibexa.api.LanguageList+json', - '_href' => '/api/ibexa/v2/languages', - 'Language' => [ - [ - '_media-type' => 'application/vnd.ibexa.api.Language+json', - '_href' => '/api/ibexa/v2/languages/eng-GB', - 'languageId' => 2, - 'languageCode' => 'eng-GB', - 'name' => 'English (United Kingdom)', - ], - [ - '_media-type' => 'application/vnd.ibexa.api.Language+json', - '_href' => '/api/ibexa/v2/languages/pol-PL', - 'languageId' => 4, - 'languageCode' => 'pol-PL', - 'name' => 'Polish (polski)', - ], - ], - ], - ], + 'example' => LANGUAGE_LIST_JSON_EXAMPLE, ], ], ], ], - summary: 'Language list', - description: 'Lists languages', ), - name: 'languages', )] #[Get( uriTemplate: '/languages/{code}', + name: 'Get language', openapi: new Model\Operation( tags: [ - 'Language attr on Controller', + 'Language', ], parameters: [ new Model\Parameter( - name: 'code', - in: 'path', - required: true, + name: 'Accept', + in: 'header', + required: 'true', + description: 'If set, the language is returned in XML or JSON format.', schema: [ 'type' => 'string', ], ), new Model\Parameter( - name: 'Accept', - description: 'If set, the language is returned in XML or JSON format.', - required: true, - in: 'header', + name: 'code', + in: 'path', + required: 'true', schema: [ - 'description' => 'If set, the language is returned in XML or JSON format.', - 'example' => 'application/vnd.ibexa.api.Language+xml -application/vnd.ibexa.api.Language+json -', 'type' => 'string', ], ), @@ -135,35 +89,18 @@ 'schema' => [ '$ref' => '#/components/schemas/Language', ], - 'example' => << - - 2 - eng-GB - English (United Kingdom) - - XML + 'example' => LANGUAGE_XML_EXAMPLE, ], 'application/vnd.ibexa.api.Language+json' => [ 'schema' => [ '$ref' => '#/components/schemas/LanguageWrapper', ], - 'example' => [ - 'Language' => [ - '_media-type' => 'application/vnd.ibexa.api.Language+json', - '_href' => '/api/ibexa/v2/languages/eng-GB', - 'languageId' => '2', - 'languageCode' => 'eng-GB', - 'name' => 'English (United Kingdom)', - ], - ], + 'example' => LANGUAGE_JSON_EXAMPLE, ], ], ], ], - summary: 'Get language', ), - name: 'languages_code', )] final class Language extends RestController { @@ -190,3 +127,64 @@ public function loadLanguage(string $languageCode): ApiLanguage return $this->languageService->loadLanguage($languageCode); } } + +const LANGUAGE_LIST_XML_EXAMPLE = << + + + 2 + eng-GB + English (United Kingdom) + + + 4 + pol-PL + Polish (polski) + + +EXAMPLE; + +const LANGUAGE_LIST_JSON_EXAMPLE = << + + 2 + eng-GB + English (United Kingdom) + +EXAMPLE; + +const LANGUAGE_JSON_EXAMPLE = << Date: Tue, 27 Aug 2024 14:17:18 +0200 Subject: [PATCH 21/23] string -> bool --- src/lib/Server/Controller/Language.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index c8d5ba23f..256d30cf4 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -29,7 +29,7 @@ new Model\Parameter( name: 'Accept', in: 'header', - required: 'true', + required: true, description: 'If set, the list is returned in XML or JSON format.', schema: [ 'type' => 'string', @@ -67,7 +67,7 @@ new Model\Parameter( name: 'Accept', in: 'header', - required: 'true', + required: true, description: 'If set, the language is returned in XML or JSON format.', schema: [ 'type' => 'string', @@ -76,7 +76,7 @@ new Model\Parameter( name: 'code', in: 'path', - required: 'true', + required: true, schema: [ 'type' => 'string', ], From 50f6a84a0ee133b4f0bcc37a9a05b5f66e3e036d Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 29 Aug 2024 19:48:23 +0200 Subject: [PATCH 22/23] introduce x-ibexa-example-file custom property --- src/bundle/ApiPlatform/OpenApiFactory.php | 102 ++++++++++++++++++ .../languages/GET/LanguageList.json.example | 21 ++++ .../languages/GET/LanguageList.xml.example | 13 +++ .../languages/code/GET/Language.json.example | 9 ++ .../languages/code/GET/Language.xml.example | 6 ++ src/bundle/Resources/config/api_platform.yml | 1 + src/lib/Server/Controller/Language.php | 69 +----------- 7 files changed, 156 insertions(+), 65 deletions(-) create mode 100644 src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example create mode 100644 src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example create mode 100644 src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example create mode 100644 src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index c9388d699..7a38dfb49 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -9,13 +9,16 @@ namespace Ibexa\Bundle\Rest\ApiPlatform; use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface; +use ApiPlatform\OpenApi\Model\Response; use ApiPlatform\OpenApi\OpenApi; +use Symfony\Component\HttpKernel\KernelInterface; final class OpenApiFactory implements OpenApiFactoryInterface { public function __construct( private readonly OpenApiFactoryInterface $decorated, private readonly SchemasCollectionFactory $schemaCollectionFactory, + private readonly KernelInterface $kernel, ) { } @@ -25,7 +28,15 @@ public function __construct( public function __invoke(array $context = []): OpenApi { $openApi = $this->decorated->__invoke($context); + $openApi = $this->addSchemas($openApi); + $this->insertExampleFilesContent($openApi); + + return $openApi; + } + + private function addSchemas(OpenApi $openApi): OpenApi + { $schemasCollection = $this->schemaCollectionFactory->create(); $schemas = iterator_to_array($schemasCollection); @@ -36,4 +47,95 @@ public function __invoke(array $context = []): OpenApi return $openApi; } + + private function insertExampleFilesContent(OpenApi $openApi): void + { + $paths = $openApi->getPaths(); + + /** @var \ApiPlatform\OpenApi\Model\PathItem $pathItem */ + foreach ($paths->getPaths() as $path => $pathItem) { + $newPathItem = $pathItem; + + /** @var array $methods */ + $methods = [ + 'GET' => $pathItem->getGet(), + 'PUT' => $pathItem->getPut(), + 'POST' => $pathItem->getPost(), + 'DELETE' => $pathItem->getDelete(), + 'OPTIONS' => $pathItem->getOptions(), + 'HEAD' => $pathItem->getHead(), + 'PATCH' => $pathItem->getPatch(), + 'TRACE' => $pathItem->getTrace(), + ]; + foreach ($methods as $method => $operation) { + if (empty($operation)) { + continue; + } + + $newOperation = $operation; + + /** + * @var int $responseCode + * @var \ApiPlatform\OpenApi\Model\Response|array> $response + */ + foreach ($operation->getResponses() as $responseCode => $response) { + if (!is_array($response) || !array_key_exists('content', $response)) { + continue; + } + + $content = $response['content']; + $newContent = $content; + + foreach ($newContent as $mediaType => $responseContent) { + if (array_key_exists('x-ibexa-example-file', $responseContent)) { + $exampleFilePath = $this->kernel->locateResource($responseContent['x-ibexa-example-file']); + $exampleFileContent = file_get_contents($exampleFilePath); + $newContent[$mediaType]['example'] = $exampleFileContent; + unset($newContent[$mediaType]['x-ibexa-example-file']); + } + } + + if ($newContent !== $content) { + $newOperation = $newOperation->withResponse( + $responseCode, + new Response((string)$responseCode, new \ArrayObject($newContent)), + ); + } + } + + if ($newOperation !== $operation) { + switch ($method) { + case 'GET': + $newPathItem = $newPathItem->withGet($newOperation); + break; + case 'PUT': + $newPathItem = $newPathItem->withPut($newOperation); + break; + case 'POST': + $newPathItem = $newPathItem->withPost($newOperation); + break; + case 'DELETE': + $newPathItem = $newPathItem->withDelete($newOperation); + break; + case 'OPTIONS': + $newPathItem = $newPathItem->withOptions($newOperation); + break; + case 'HEAD': + $newPathItem = $newPathItem->withHead($newOperation); + break; + case 'PATCH': + $newPathItem = $newPathItem->withPatch($newOperation); + break; + case 'TRACE': + $newPathItem = $newPathItem->withTrace($newOperation); + break; + } + } + } + + if ($newPathItem !== $pathItem) { + $paths->addPath($path, $newPathItem); + } + } + } } diff --git a/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example new file mode 100644 index 000000000..e55d78560 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example @@ -0,0 +1,21 @@ +{ + "LanguageList": { + "_media-type": "application/vnd.ibexa.api.LanguageList+json", + "_href": "/api/ibexa/v2/languages", + "Language": [ + { + "_media-type": "application/vnd.ibexa.api.Language+json", + "_href": "/api/ibexa/v2/languages/eng-GB", + "languageId": 2, + "languageCode": "eng-GB", + "name": "English (United Kingdom)" + }, { + "_href": "/api/ibexa/v2/languages/pol-PL", + "_media-type": "application/vnd.ibexa.api.Language+json", + "languageCode": "pol-PL", + "languageId": 4, + "name": "Polish (polski)" + } + ] + } +} diff --git a/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example new file mode 100644 index 000000000..f61cfa725 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example @@ -0,0 +1,13 @@ + + + + 2 + eng-GB + English (United Kingdom) + + + 4 + pol-PL + Polish (polski) + + diff --git a/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example new file mode 100644 index 000000000..6c00456f2 --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.json.example @@ -0,0 +1,9 @@ +{ + "Language": { + "_media-type": "application/vnd.ibexa.api.Language+json", + "_href": "/api/ibexa/v2/languages/eng-GB", + "languageId": 2, + "languageCode": "eng-GB", + "name": "English (United Kingdom)" + } +} diff --git a/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example new file mode 100644 index 000000000..43519f1cd --- /dev/null +++ b/src/bundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example @@ -0,0 +1,6 @@ + + + 2 + eng-GB + English (United Kingdom) + diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index a0f4f75d6..e454b08a7 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -21,6 +21,7 @@ services: arguments: - '@.inner' - '@Ibexa\Bundle\Rest\ApiPlatform\SchemasCollectionFactory' + - '@Symfony\Component\HttpKernel\KernelInterface' ibexa.api_platform.openapi.factory: parent: api_platform.openapi.factory diff --git a/src/lib/Server/Controller/Language.php b/src/lib/Server/Controller/Language.php index 256d30cf4..882077816 100644 --- a/src/lib/Server/Controller/Language.php +++ b/src/lib/Server/Controller/Language.php @@ -43,13 +43,13 @@ 'schema' => [ '$ref' => '#/components/schemas/LanguageList', ], - 'example' => LANGUAGE_LIST_XML_EXAMPLE, + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/GET/LanguageList.xml.example', ], 'application/vnd.ibexa.api.LanguageList+json' => [ 'schema' => [ '$ref' => '#/components/schemas/LanguageListWrapper', ], - 'example' => LANGUAGE_LIST_JSON_EXAMPLE, + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/GET/LanguageList.json.example', ], ], ], @@ -89,13 +89,13 @@ 'schema' => [ '$ref' => '#/components/schemas/Language', ], - 'example' => LANGUAGE_XML_EXAMPLE, + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/code/GET/Language.xml.example', ], 'application/vnd.ibexa.api.Language+json' => [ 'schema' => [ '$ref' => '#/components/schemas/LanguageWrapper', ], - 'example' => LANGUAGE_JSON_EXAMPLE, + 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/languages/code/GET/Language.json.example', ], ], ], @@ -127,64 +127,3 @@ public function loadLanguage(string $languageCode): ApiLanguage return $this->languageService->loadLanguage($languageCode); } } - -const LANGUAGE_LIST_XML_EXAMPLE = << - - - 2 - eng-GB - English (United Kingdom) - - - 4 - pol-PL - Polish (polski) - - -EXAMPLE; - -const LANGUAGE_LIST_JSON_EXAMPLE = << - - 2 - eng-GB - English (United Kingdom) - -EXAMPLE; - -const LANGUAGE_JSON_EXAMPLE = << Date: Wed, 4 Sep 2024 12:59:12 +0200 Subject: [PATCH 23/23] null check --- src/bundle/ApiPlatform/OpenApiFactory.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bundle/ApiPlatform/OpenApiFactory.php b/src/bundle/ApiPlatform/OpenApiFactory.php index 7a38dfb49..595edbd7f 100644 --- a/src/bundle/ApiPlatform/OpenApiFactory.php +++ b/src/bundle/ApiPlatform/OpenApiFactory.php @@ -72,6 +72,12 @@ private function insertExampleFilesContent(OpenApi $openApi): void continue; } + $responses = $operation->getResponses(); + + if ($responses === null) { + continue; + } + $newOperation = $operation; /**