From 128ccf95177d3e112644686532613d2a3cf08798 Mon Sep 17 00:00:00 2001 From: Maxime Veber Date: Fri, 10 Dec 2021 17:11:45 +0100 Subject: [PATCH 1/3] feat: new way to configure doc path In the case of full managed applications, the app is often build in a folder then moved to another. The routing is resolved and cached at build time. Therefore using the routing is not a good option to give a static path. To avoid that we use the configuration instead. --- UPGRADE-0.9.md | 4 ++++ src/DependencyInjection/Configuration.php | 2 ++ .../Controller/SwaggerUiController.php | 20 +++++++++++++++++-- src/MelodiiaConfiguration.php | 1 + src/MelodiiaConfigurationInterface.php | 2 +- src/Resources/config/twig.yaml | 1 + .../Controller/SwaggerUiControllerTest.php | 20 +++++++++++++------ tests/TestApplication/config/melodiia.yaml | 3 ++- tests/TestApplication/config/routing_dev.yaml | 2 -- 9 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 UPGRADE-0.9.md diff --git a/UPGRADE-0.9.md b/UPGRADE-0.9.md new file mode 100644 index 0000000..c300d0d --- /dev/null +++ b/UPGRADE-0.9.md @@ -0,0 +1,4 @@ +Upgrade guide 0.8...0.9 +======================= + +Use the new configuration key `openapi_path` instead of `documentation_file_path` to configure your documentation routing. diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 784fff8..9ad2907 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -4,6 +4,7 @@ namespace SwagIndustries\Melodiia\DependencyInjection; +use SwagIndustries\Melodiia\MelodiiaConfiguration; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -28,6 +29,7 @@ public function getConfigTreeBuilder() ->arrayPrototype() ->children() ->scalarNode('base_path')->defaultValue('/')->end() + ->scalarNode(MelodiiaConfiguration::CONFIGURATION_OPENAPI_PATH)->defaultNull()->end() ->arrayNode('pagination') ->addDefaultsIfNotSet() ->children() diff --git a/src/Documentation/Controller/SwaggerUiController.php b/src/Documentation/Controller/SwaggerUiController.php index 4da8782..b81c7b6 100644 --- a/src/Documentation/Controller/SwaggerUiController.php +++ b/src/Documentation/Controller/SwaggerUiController.php @@ -6,6 +6,8 @@ use SwagIndustries\Melodiia\Exception\MelodiiaLogicException; use SwagIndustries\Melodiia\Exception\MelodiiaRuntimeException; +use SwagIndustries\Melodiia\MelodiiaConfiguration; +use SwagIndustries\Melodiia\MelodiiaConfigurationInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Yaml\Exception\ParseException; @@ -14,21 +16,35 @@ class SwaggerUiController { + /** @deprecated use melodiia configuration instead */ public const PATH_TO_OPEN_API_FILE_OPTION = 'documentation_file_path'; + /** * @var Environment */ private $templating; - public function __construct(Environment $templating) + /** @var MelodiiaConfigurationInterface */ + private $configuration; + + public function __construct(Environment $templating, MelodiiaConfigurationInterface $configuration) { $this->templating = $templating; + $this->configuration = $configuration; } public function __invoke(Request $request) { $response = new Response(); - $path = $request->attributes->get(self::PATH_TO_OPEN_API_FILE_OPTION, null); + + $path = $this->configuration->getApiConfigFor($request)[MelodiiaConfiguration::CONFIGURATION_OPENAPI_PATH] ?? null; + + if (null === $path) { + $path = $request->attributes->get(self::PATH_TO_OPEN_API_FILE_OPTION, null); + if (null !== $path) { + @trigger_error('Using a route attribute to define the documentation path is deprecated since Melodiia 0.9.0 and will be removed in 1.0.0.', \E_USER_DEPRECATED); + } + } if (null === $path) { throw new MelodiiaLogicException(sprintf('The option %s is missing on the documentation route', self::PATH_TO_OPEN_API_FILE_OPTION)); diff --git a/src/MelodiiaConfiguration.php b/src/MelodiiaConfiguration.php index 19391f6..d25a666 100644 --- a/src/MelodiiaConfiguration.php +++ b/src/MelodiiaConfiguration.php @@ -9,6 +9,7 @@ final class MelodiiaConfiguration implements MelodiiaConfigurationInterface { public const PREFIX_CONTROLLER = 'melodiia.crud.controller'; + public const CONFIGURATION_OPENAPI_PATH = 'openapi_path'; /** * @var array diff --git a/src/MelodiiaConfigurationInterface.php b/src/MelodiiaConfigurationInterface.php index 8cd2964..4745d90 100644 --- a/src/MelodiiaConfigurationInterface.php +++ b/src/MelodiiaConfigurationInterface.php @@ -7,7 +7,7 @@ use Symfony\Component\HttpFoundation\Request; /** - * @internal inheritance + * @internal for testing purpose, you should implement this interface */ interface MelodiiaConfigurationInterface { diff --git a/src/Resources/config/twig.yaml b/src/Resources/config/twig.yaml index cee4019..f3555e2 100644 --- a/src/Resources/config/twig.yaml +++ b/src/Resources/config/twig.yaml @@ -4,3 +4,4 @@ services: tags: ['controller.service_arguments'] arguments: $templating: '@twig' + $configuration: '@melodiia.configuration' diff --git a/tests/Melodiia/Documentation/Controller/SwaggerUiControllerTest.php b/tests/Melodiia/Documentation/Controller/SwaggerUiControllerTest.php index 806499f..26aa7c4 100644 --- a/tests/Melodiia/Documentation/Controller/SwaggerUiControllerTest.php +++ b/tests/Melodiia/Documentation/Controller/SwaggerUiControllerTest.php @@ -8,6 +8,8 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use SwagIndustries\Melodiia\Documentation\Controller\SwaggerUiController; +use SwagIndustries\Melodiia\MelodiiaConfiguration; +use SwagIndustries\Melodiia\MelodiiaConfigurationInterface; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -28,13 +30,19 @@ public function setUp(): void public function testItGeneratesAResponseUsingTemplate() { - $attributes = new ParameterBag(); - $attributes->set(SwaggerUiController::PATH_TO_OPEN_API_FILE_OPTION, __DIR__ . '/../../../fixtures/doc.yaml'); - $this->templating->render(Argument::type('string'), ['json' => '{"openapi":"3.0.1"}'])->shouldBeCalled(); $request = $this->prophesize(Request::class); - $request->attributes = $attributes; - $this->controller = new SwaggerUiController($this->templating->reveal()); + $request->attributes = new ParameterBag(); + $request = $request->reveal(); + /** @var MelodiiaConfigurationInterface|ObjectProphecy $config */ + $config = $this->prophesize(MelodiiaConfigurationInterface::class); + $config->getApiConfigFor($request)->willReturn([ + MelodiiaConfiguration::CONFIGURATION_OPENAPI_PATH => __DIR__ . '/../../../fixtures/doc.yaml', + ]); + + $this->templating->render(Argument::type('string'), ['json' => '{"openapi":"3.0.1"}'])->shouldBeCalled(); + + $this->controller = new SwaggerUiController($this->templating->reveal(), $config->reveal()); - $this->assertInstanceOf(Response::class, $this->controller->__invoke($request->reveal())); + $this->assertInstanceOf(Response::class, $this->controller->__invoke($request)); } } diff --git a/tests/TestApplication/config/melodiia.yaml b/tests/TestApplication/config/melodiia.yaml index 6d20b87..cb102ab 100644 --- a/tests/TestApplication/config/melodiia.yaml +++ b/tests/TestApplication/config/melodiia.yaml @@ -1,3 +1,4 @@ melodiia: apis: - main: ~ + main: + openapi_path: '%kernel.project_dir%/config/documentation.yaml' diff --git a/tests/TestApplication/config/routing_dev.yaml b/tests/TestApplication/config/routing_dev.yaml index fb0ef74..7918c18 100644 --- a/tests/TestApplication/config/routing_dev.yaml +++ b/tests/TestApplication/config/routing_dev.yaml @@ -2,5 +2,3 @@ documentation: path: /documentation controller: 'melodiia.documentation' - defaults: - documentation_file_path: '%kernel.project_dir%/config/documentation.yaml' From 6add5a871ab175e05d1c9a453b57ea31172881f8 Mon Sep 17 00:00:00 2001 From: Maxime Veber Date: Fri, 10 Dec 2021 17:14:20 +0100 Subject: [PATCH 2/3] feat: add depedency missing exception Since the documentation path is filled, it means that the documentation controller will be rendered, which needs twig to work. This exception is an early warning. I feel comfortable with it! --- src/DependencyInjection/MelodiiaExtension.php | 15 ++++++++++++--- src/Exception/DependencyMissingException.php | 9 +++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/Exception/DependencyMissingException.php diff --git a/src/DependencyInjection/MelodiiaExtension.php b/src/DependencyInjection/MelodiiaExtension.php index b7364e3..2d40cd0 100644 --- a/src/DependencyInjection/MelodiiaExtension.php +++ b/src/DependencyInjection/MelodiiaExtension.php @@ -6,6 +6,8 @@ use Doctrine\Persistence\AbstractManagerRegistry; use SwagIndustries\Melodiia\Crud\FilterInterface; +use SwagIndustries\Melodiia\Exception\DependencyMissingException; +use SwagIndustries\Melodiia\MelodiiaConfiguration; use SwagIndustries\Melodiia\Serialization\Context\ContextBuilderInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -28,6 +30,9 @@ public function load(array $configs, ContainerBuilder $container) $xmlLoader = new XmlFileLoader($container, $configFileLocator); $xmlLoader->load('error-management.xml'); + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + if (class_exists(AbstractManagerRegistry::class)) { $loader->load('doctrine.yaml'); } @@ -41,11 +46,15 @@ public function load(array $configs, ContainerBuilder $container) if (class_exists(Environment::class)) { $loader->load('twig.yaml'); + } elseif ('dev' === $container->getParameter('kernel.environment')) { + // This is just a helpful layer in case some dependency is missing, because twig is optional. + foreach ($config['api'] as $endpoint) { + if (!empty($endpoint[MelodiiaConfiguration::CONFIGURATION_OPENAPI_PATH])) { + throw new DependencyMissingException('You specified a documentation path but twig is not activated. Melodiia will not be able to render your documentation.'); + } + } } - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); - $container->setParameter('melodiia.config', $config); // Autoconf diff --git a/src/Exception/DependencyMissingException.php b/src/Exception/DependencyMissingException.php new file mode 100644 index 0000000..a1e270e --- /dev/null +++ b/src/Exception/DependencyMissingException.php @@ -0,0 +1,9 @@ + Date: Fri, 10 Dec 2021 18:39:11 +0100 Subject: [PATCH 3/3] feat: update documentation following last changes --- CHANGELOG.md | 9 ++++++++- docs/config-reference.md | 1 + docs/getting-started.md | 18 +++++++++++++++--- src/DependencyInjection/MelodiiaExtension.php | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0a858e..5ea37d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.8.0] 2020-11-26 +## [0.9.0] 2021-12-10 +### Added +- Compatibility with PHP 8.1 + +### Changed +- Fix #54 to be able to build a melodiia application on any PAAS service + +## [0.8.0] 2021-11-26 ### Added - Add json-api header in responses diff --git a/docs/config-reference.md b/docs/config-reference.md index 88b71f0..b90e372 100644 --- a/docs/config-reference.md +++ b/docs/config-reference.md @@ -9,6 +9,7 @@ melodiia: # Define as much APIs you want here. main: base_path: /api/v1 + openapi_path: /path/to/your/openapi/doc.yaml pagination: # Using this query attribute you can change the max per page max_per_page_attribute: max_per_page diff --git a/docs/getting-started.md b/docs/getting-started.md index 3489e24..f4de21b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -113,11 +113,23 @@ to configure the OpenAPI documentation in a good way was a serious pain and was But it comes with some help to build your documentation: 1. Run `bin/console assets:insall` 2. Create a file `documentation.yml` (the `config` folder seems like a good location) -2. Add the following routing to your configuration +3. Add your documentation file in the global melodiia configuration and configure your documentation route ```yaml -# The documentation should be available only in dev environment -# routing_dev.yaml +# /config/packages/melodiia.yaml +melodiia: + apis: + main: + # Required all the time! Do not forget this! + base_path: /api/v1 + + # This is what we are looking for here: + openapi_path: /path/to/your/openapi/doc.yaml +``` + +```yaml +# The documentation should be available only in dev environment in some cases +# /config/routing_dev.yaml documentation: path: /documentation controller: 'melodiia.documentation' diff --git a/src/DependencyInjection/MelodiiaExtension.php b/src/DependencyInjection/MelodiiaExtension.php index 2d40cd0..298e385 100644 --- a/src/DependencyInjection/MelodiiaExtension.php +++ b/src/DependencyInjection/MelodiiaExtension.php @@ -50,7 +50,7 @@ public function load(array $configs, ContainerBuilder $container) // This is just a helpful layer in case some dependency is missing, because twig is optional. foreach ($config['api'] as $endpoint) { if (!empty($endpoint[MelodiiaConfiguration::CONFIGURATION_OPENAPI_PATH])) { - throw new DependencyMissingException('You specified a documentation path but twig is not activated. Melodiia will not be able to render your documentation.'); + throw new DependencyMissingException('You specified a documentation path but twig is not installed. Melodiia will not be able to render your documentation.'); } } }