Skip to content

Commit

Permalink
Merge pull request #27 from onmoon/update-docs
Browse files Browse the repository at this point in the history
Update docs
  • Loading branch information
DimanKuskov authored Feb 13, 2020
2 parents fc12af0 + d33a5d2 commit c88090c
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 37 deletions.
142 changes: 110 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
# Symfony OpenApi Server Bundle

## About

This bundle can generate most of the usual boilerplate code you write when implementing an API.
The code is generated from OpenAPI specifications.

The following concerns are handled by the bundle automatically:
- Route generation and routing
- Validation of incoming requests against the specification
- Strictly-typed request and response objects and API call handlers interfaces
- Calling your code containing the API call handling logic passing the request object
- Serializing of the returned response object

All you have to do is to implement the API call handler interfaces and return the provided response object.

## Installation

The preferred way to install this extension is through [composer](http://getcomposer.org/download/).

Either run
Run

```
composer require onmoon/openapi-server-bundle
```

or add

```
"onmoon/openapi-server-bundle": "^0.0"
```

to the require section of your `composer.json` file.

Then add
Then add the bundle class to your `config/bundles.php`:
```php
OnMoon\OpenApiServerBundle\OpenApiServerBundle::class => ['all' => true],
```
to array in `config/bundles.php`.

## Usage

You can configure the bundle by adding the following parameters to your `/config/packages/open_api_server.yaml`

```yaml
open_api_server:
# root_name_space: App\Generated # NameSpace for DTOs and Api Interfaces
## We will try to derive path for generated files from namespace. If you do not want them to be
## stored in App namespace or if you App namespace is not in %kernel.project_dir%/src/, then you
## can specify path manually:
# root_path: %kernel.project_dir%/src/Generated
# language_level: 7.4.0 # minimum PHP version the generated code should be compatible with
# generated_dir_permissions: 0755 # permissions for the generated directories
root_name_space: App\Generated # Namespace for DTOs and Api Interfaces
## The bundle will try to derive the paths for the generated files from the namespace. If you do not want them to be
## stored in \App namespace or if you \App namespace is not in %kernel.project_dir%/src/, then you
## can specify this path manually:
root_path: %kernel.project_dir%/src/Generated
language_level: 7.4.0 # minimum PHP version the generated code should be compatible with
generated_dir_permissions: 0755 # permissions for the generated directories
specs:
petstore:
path: '../spec/petstore.yaml' # path to OpenApi specification
# type: yaml # Specification format, either yaml or json. Extension is used if omitted
name_space: PetStore # NameSpace for generated DTOs and Interfaces
type: yaml # Specification format, either yaml or json. If omitted, the specification file extension will be used.
name_space: PetStore # Namespace for generated DTOs and Interfaces
media_type: 'application/json' # media type from the specification files to use for generating request and response DTOs
```
Expand All @@ -50,25 +55,98 @@ with `open_api` type:

```yaml
petstore-api:
resource: 'petstore' # This should be same as in specs section of bundle config
resource: 'petstore' # This should be same as in specs section of /config/packages/open_api_server.yaml
type: open_api
# prefix: '/api' # Add this standard parameter to add base path to all paths in api
# name_prefix: 'petstore_' # This will add prefix to route names
prefix: '/api' # Add this standard parameter to add base path to all paths in api
name_prefix: 'petstore_' # This will add a prefix to route names
```

After configuring the bundle you can generate the API server code and implement the generated service interfaces
with code that should handle the api calls.
## Requirements for your OpenAPI schemas

## Commands

- Generate the server code: `php bin/console open-api:generate`
- Refresh the server code: `php bin/console open-api:refresh`
- Delete the server code: `php bin/console open-api:delete`

## Limitations:
For the bundle to work properly with your specifications, they should be written in OpenAPI 3.0 format and each
operation must have an unique `operationId`.

Currently, there are also the following limitations:
- `number` without `format` is treated as float
- Only scalar types are allowed in path parameters
- Partial match pattern are ignored in path parameter patterns, only `^...$` patterns are used
- If pattern is specified in path parameter then type- and format-generated requirements are ignored
- Only one media-type can be used for request and response body schemas. See: https://swagger.io/docs/specification/media-types/

## Generating the API Server code

There are three console commands that work with the generated API server code:

- Generate the server code: `php bin/console open-api:generate`
- Refresh the server code: `php bin/console open-api:refresh`
- Delete the server code: `php bin/console open-api:delete`

Most of the time you should use the `refresh` command.
It will clear the bundle cache, delete the old generated server code if it exists and generate the new code.

Be careful with the refresh and delete commands, they will delete the entire contents of the directory you have specified
in `root_name_space` in the `/config/packages/open_api_server.yaml` file. That directory should contain no files except
the code generated by this bundle, as it will be deleted every time you generate the API server code.

For each operation described in the specification, a API call handler interface will be generated that you should implement
to handle the API calls.

## Implementing the API call handlers interfaces

Given the following generated API handler interface:
```php
<?php
declare (strict_types=1);
namespace App\Generated\Apis\PetStore\ShowPetById;
use OnMoon\OpenApiServerBundle\Interfaces\Service;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Request\ShowPetByIdRequestDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\ShowPetByIdResponse;
/**
* This interface was automatically generated
* You should not change it manually as it will be overwritten
*/
interface ShowPetById extends Service
{
/** Info for a specific pet */
public function showPetById(ShowPetByIdRequestDto $request) : ShowPetByIdResponse;
}
```

Your API call handler could look like this:
```php
<?php
namespace App\Api;
use App\Repository\PetRepository;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Request\ShowPetByIdRequestDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\OK\ShowPetByIdResponseDto;
use App\Generated\Apis\PetStore\ShowPetById\Dto\Response\ShowPetByIdResponse;
use App\Generated\Apis\PetStore\ShowPetById\ShowPetById;
class ShowPetByIdHandler implements ShowPetById
{
private PetRepository $pets;
public function __construct(PetRepository $pets)
{
$this->pets = $pets;
}
public function showPetById(ShowPetByIdRequestDto $request) : ShowPetByIdResponse
{
$petId = $request->getPathParameters()->getPetId();
$pet = $this->pets->getById($petId);
return new ShowPetByIdResponseDto($pet->id(), $pet->name());
}
}
```

Additionally, your API call handler can implement the following interfaces:
- `\OnMoon\OpenApiServerBundle\Interfaces\SetClientIp` - if it needs the client IP address
- `\OnMoon\OpenApiServerBundle\Interfaces\SetRequest` - if it needs the Symfony request object
5 changes: 1 addition & 4 deletions src/CodeGenerator/ApiServerCodeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
class ApiServerCodeGenerator
{
public const APIS_NAMESPACE = 'Apis';
public const SERVICE_SUFFIX = 'ServiceInterface';
private const DTO_NAMESPACE = 'Dto';
private const SERVICE_SUBSCRIBER_NAMESPACE = 'ServiceSubscriber';
private const SERVICE_SUBSCRIBER_CLASSNAME = 'ApiServiceLoaderServiceSubscriber';
Expand Down Expand Up @@ -318,9 +317,7 @@ public function generate() : void
$serviceInterfaceNamesapce = $this->namingStrategy->buildNamespace($apiNamespace, $operationName);
$serviceInterfacePath = $this->namingStrategy->buildPath($apiPath, $operationName);

$serviceInterfaceClassName = $this->namingStrategy->stringToNamespace(
$operationName . self::SERVICE_SUFFIX
);
$serviceInterfaceClassName = $this->namingStrategy->stringToNamespace($operationName);
$serviceInterfaceMethod = $this->namingStrategy->stringToMethodName($operationId);
$serviceInterfaceFileName = $serviceInterfaceClassName . '.php';

Expand Down
2 changes: 1 addition & 1 deletion src/CodeGenerator/Naming/DefaultNamingStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function getInterfaceFQCN(string $apiNameSpace, string $operationId) : st
ApiServerCodeGenerator::APIS_NAMESPACE,
$apiNameSpace,
$this->stringToNamespace($operationId),
$this->stringToNamespace($operationId) . ApiServerCodeGenerator::SERVICE_SUFFIX,
$this->stringToNamespace($operationId),
);

return $interfaceNamespace;
Expand Down

0 comments on commit c88090c

Please sign in to comment.