zolteam / ogen
Requires
- php: >=8.1
- nikic/php-parser: ^4.16 || ^5.0
- phpstan/phpdoc-parser: ^1.21
- symfony/string: ^6.3 || ^7.0
- symfony/uid: ^6.3 || ^7.0
- symfony/yaml: ^6.3 || ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: 3.62.0
- phpstan/extension-installer: 1.4.1
- phpstan/phpstan: 1.11.10
- phpstan/phpstan-doctrine: 1.5.0
- phpstan/phpstan-phpunit: 1.4.0
- phpstan/phpstan-symfony: 1.4.6
- phpunit/phpunit: 9.5.5
- symfony/filesystem: ^6.3 || ^7.0
- symfony/framework-bundle: ^6.3 || ^7.0
- symfony/phpunit-bridge: ^6.3 || ^7.0
- symfony/test-pack: ^1.1
- zol/test-openapi-server: @dev
README
Ogen stands for OpenAPI Generator and is a tool that generates a Symfony bundle from an OpenAPI specification YAML file. The bundle is a server stub implementing the specified API: it automatically handles all the routing as well as the (de)serialization of the DTOs and their validation. You then have to plug the domain behaviour by implementing interfaces defined by the bundle.
Usage
To use Ogen, you first need to install the latest version of this vendor in your project:
make composer-bash
composer require --dev zolteam/ogen:^1.0
exit
Then, add a target in your Makefile:
ogen:
rm -rf symfony/openapi/invoicing/bundle/*
wget -O symfony/openapi/invoicing/openapi.yaml https://stoplight.io/api/v1/projects/bfav-zol/zol-skeleton/nodes/zol-skeleton.yaml
@$(RUNNER_DOCKER_EXEC) 'vendor/bin/ogen InvoicingOpenApiServer Zol\\Invoicing\\OpenApiServer zol/invoicing-openapi-server /var/www/html/openapi/invoicing/openapi.yaml /var/www/html/openapi/invoicing/bundle'
make cc
- Replace all
symfony/openapi/invoicing/bundle
by the path of the folder where you want the bundle code to be generated. - Replace all
symfony/openapi/invoicing/openapi.yaml
by the path where you want a copy of the OpenApi specification YAML file. - Replace
https://stoplight.io/api/v1/projects/bfav-zol/zol-skeleton/nodes/zol-skeleton.yaml
by the URL to your OpenAPI specification YAML file (in this example we are using stoplight.io as an editor for our specification). - Replace
InvoicingOpenApiServer
by the name you want to give to your bundle. - Replace
Zol\\Invoicing\\OpenApiServer
by the namespace you want the classes of your bundle to live in. - Replace
zol/invoicing-openapi-server
by the name you want for the bundle's composer package.
Then, run the target:
make ogen
From now on, you can overwrite the bundle anytime you update the OpenAPI specification by replaying the previous command. You should never modify any file in the bundle as they will be overwritten each time you regenerate the bundle.
The bundle is now generated and needs to be configured. First add the bundle directory as a repository in the composer.json of your project:
"repositories": [
{
"type": "path",
"url": "openapi/invoicing/bundle"
}
]
Then you can require the bundle:
make composer-bash
composer require zol/invoicing-openapi-server:dev-main
composer update
exit
Next, add the bundle in the symfony/config/bundles.php file:
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
\Zol\Invoicing\OpenApiServer\InvoicingOpenApiServerBundle::class => ['all' => true], // <-- Here
];
Then, add the bundle routes in your symfony/config/routes.yaml file choosing any prefix:
parazol:
resource: '@InvoicingOpenApiServerBundle/config/routes.yaml'
prefix: 'api'
Now you have to implement all interfaces you find in your bundle, that is all
src/Api/<TagName>/<TagName>Handler.php
and all src/Format/<FormatName>Definition.php
.
Once implemented, you have to declare them as services and tag them appropriately in your services.yaml file.
Here is an example:
<?php
// symfony/src/Zol/Invoicing/Presentation/Client/ClientHandler.php
namespace App\Zol\Invoicing\Presentation\Client;
use Zol\Invoicing\OpenApiServer\Api\Client\GetClients200ApplicationJsonResponse;
class ClientHandler implements \Zol\Invoicing\OpenApiServer\Api\Client\ClientHandler
{
// ...
}
<?php
// symfony/src/Zol/Invoicing/Presentation/UuidDefinition.php
namespace App\Zol\Invoicing\Presentation;
class UuidDefinition implements \Zol\Invoicing\OpenApiServer\Format\UuidDefinition
{
public function validate(mixed $value): array
{
// ...
}
}
# symfony/config/services.yaml
### Handlers ###
App\Zol\Invoicing\Presentation\Client\ClientHandler:
tags:
- { name: "invoicing_open_api_server.handler", controller: "client" }
### Format Definitions ###
App\Zol\Invoicing\Presentation\UuidDefinition:
tags:
- { name: "invoicing_open_api_server.format_definition", format: "uuid" }
That's it, you can now test calling your endpoints !
Troubleshooting
My endpoints are all listed under the DefaultHandler
If you tag your operation then it will be listed under the
My endpoint does not appear nor in the routes nor in the handlers generated
Make sure you declared at least one response with a non-empty content in the endpoint specification. You can always specify a 400 response for example, even if you never intend to return it in your implementation.
My handlers' method names are meaningless
The operation name is used to generate them so make them clear and simple like e.g. "get-client".