Skip to content

Commit

Permalink
feat: async api schema v3 without operations (#23)
Browse files Browse the repository at this point in the history
Async API V3 adds an Operation object. It gives many more features but
also increases the complexity. Therefore, this PR introduces Schema V3
compatibility, but without operations.
  • Loading branch information
Ferror authored Feb 3, 2024
1 parent 34c460d commit 5eef556
Show file tree
Hide file tree
Showing 19 changed files with 566 additions and 34 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
name: CI - PHP ${{ matrix.php }}, Dependencies ${{ matrix.dependencies }}
name: CI - PHP ${{ matrix.php }}, Dependencies ${{ matrix.dependencies }}, Schema ${{ matrix.schema }}
env:
ASYNCAPI_VERSION: ${{ matrix.schema }}
strategy:
matrix:
php: [8.2, 8.3]
schema: [3.0.0, 2.6.0]
dependencies: [lowest, highest]
include:
-
Expand Down Expand Up @@ -55,12 +58,12 @@ jobs:
- # Run AsyncAPI Validation
name: AsyncAPI Validation
run: |
docker run --rm -v $(pwd):/app asyncapi/cli:1.4.4 validate /app/var/asyncapi.yaml
docker run --rm -v $(pwd):/app asyncapi/cli:1.4.4 validate /app/var/asyncapi.json
docker run --rm -v $(pwd):/app asyncapi/cli:1.4.4 validate /app/var/${{ matrix.schema }}/asyncapi.yaml
docker run --rm -v $(pwd):/app asyncapi/cli:1.4.4 validate /app/var/${{ matrix.schema }}/asyncapi.json
- # Upload Coverage to Coveralls
name: Coveralls
if: ${{ matrix.coveralls }}
if: ${{ matrix.coveralls && github.event_name == 'pull_request' }}
uses: coverallsapp/[email protected]
with:
file: var/coverage/clover.xml
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

<php>
<env name="KERNEL_CLASS" value="Ferror\AsyncapiDocBundle\Tests\Integration\Service\Kernel" />
<env name="ASYNCAPI_VERSION" value="3.0.0" />
</php>

<source
Expand Down
4 changes: 2 additions & 2 deletions src/Generator/GeneratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

use Ferror\AsyncapiDocBundle\DataFormat;
use Ferror\AsyncapiDocBundle\GeneratorInterface;
use Ferror\AsyncapiDocBundle\Schema\V2\SchemaRenderer;
use Ferror\AsyncapiDocBundle\SchemaRendererInterface;

final readonly class GeneratorFactory
{
public function __construct(private SchemaRenderer $generator)
public function __construct(private SchemaRendererInterface $generator)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/Generator/JsonGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
namespace Ferror\AsyncapiDocBundle\Generator;

use Ferror\AsyncapiDocBundle\GeneratorInterface;
use Ferror\AsyncapiDocBundle\Schema\V2\SchemaRenderer;
use Ferror\AsyncapiDocBundle\SchemaRendererInterface;
use JsonException;

final readonly class JsonGenerator implements GeneratorInterface
{
public function __construct(private SchemaRenderer $generator)
public function __construct(private SchemaRendererInterface $generator)
{
}

Expand Down
4 changes: 2 additions & 2 deletions src/Generator/YamlGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
namespace Ferror\AsyncapiDocBundle\Generator;

use Ferror\AsyncapiDocBundle\GeneratorInterface;
use Ferror\AsyncapiDocBundle\Schema\V2\SchemaRenderer;
use Ferror\AsyncapiDocBundle\SchemaRendererInterface;
use Symfony\Component\Yaml\Yaml;

final readonly class YamlGenerator implements GeneratorInterface
{
public function __construct(private SchemaRenderer $generator)
public function __construct(private SchemaRendererInterface $generator)
{
}

Expand Down
10 changes: 8 additions & 2 deletions src/Schema/SchemaGeneratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

namespace Ferror\AsyncapiDocBundle\Schema;

use Ferror\AsyncapiDocBundle\Schema\V2\SchemaRenderer;
use Ferror\AsyncapiDocBundle\Schema\V2\SchemaRenderer as SchemaV2Renderer;
use Ferror\AsyncapiDocBundle\Schema\V3\SchemaRenderer as SchemaV3Renderer;
use Ferror\AsyncapiDocBundle\SchemaRendererInterface;
use InvalidArgumentException;

final readonly class SchemaGeneratorFactory
{
public function __construct(
private SchemaRenderer $schemaGeneratorV2,
private SchemaV2Renderer $schemaGeneratorV2,
private SchemaV3Renderer $schemaGeneratorV3,
) {
}

Expand All @@ -23,6 +25,10 @@ public function __invoke(string $version): SchemaRendererInterface
return $this->schemaGeneratorV2;
}

if ($major === '3') {
return $this->schemaGeneratorV3;
}

throw new InvalidArgumentException("Not supported Async API Schema $version");
}
}
25 changes: 25 additions & 0 deletions src/Schema/V3/ChannelRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Ferror\AsyncapiDocBundle\Schema\V3;

final readonly class ChannelRenderer
{
public function render(array $document): array
{
$channels = [];

foreach ($document['channels'] as $channel) {
$channels[$channel['name']] = [
'messages' => [
$document['name'] => [
'$ref' => '#/components/messages/' . $document['name'],
]
],
];
}

return $channels;
}
}
24 changes: 24 additions & 0 deletions src/Schema/V3/InfoRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Ferror\AsyncapiDocBundle\Schema\V3;

final readonly class InfoRenderer
{
public function __construct(
public string $title,
public string $description,
public string $version,
) {
}

public function render(): array
{
return [
'title' => $this->title,
'version' => $this->version,
'description' => $this->description,
];
}
}
46 changes: 46 additions & 0 deletions src/Schema/V3/MessageRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Ferror\AsyncapiDocBundle\Schema\V3;

use Ferror\AsyncapiDocBundle\PropertyTypeTranslator;

final readonly class MessageRenderer
{
public function render(array $document): array
{
$properties = [];
$required = [];

foreach ($document['properties'] as $property) {
$properties[$property['name']]['type'] = PropertyTypeTranslator::translate($property['type']);

if (!empty($property['description'])) {
$properties[$property['name']]['description'] = $property['description'];
}

if (!empty($property['format'])) {
$properties[$property['name']]['format'] = $property['format'];
}

if (!empty($property['example'])) {
$properties[$property['name']]['example'] = $property['example'];
}

if (isset($property['required']) && $property['required']) {
$required[] = $property['name'];
}
}

$message[$document['name']] = [
'payload' => [
'type' => 'object',
'properties' => $properties,
'required' => $required,
],
];

return $message;
}
}
50 changes: 48 additions & 2 deletions src/Schema/V3/SchemaRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,59 @@

namespace Ferror\AsyncapiDocBundle\Schema\V3;

use Ferror\AsyncapiDocBundle\ClassFinder\ClassFinderInterface;
use Ferror\AsyncapiDocBundle\DocumentationEditor;
use Ferror\AsyncapiDocBundle\SchemaRendererInterface;
use RuntimeException;

final readonly class SchemaRenderer implements SchemaRendererInterface
{
public function __construct(
private ClassFinderInterface $classFinder,
private DocumentationEditor $documentationEditor,
private InfoRenderer $infoRenderer,
private MessageRenderer $messageRenderer,
private ChannelRenderer $channelRenderer,
private ServerRenderer $serverRenderer,
private string $schemaVersion,
) {
}

public function generate(): array
{
throw new RuntimeException("Async API V3 not yet supported");
$classes = $this->classFinder->find();

$channels = [];
$messages = [];

foreach ($classes as $class) {
$document = $this->documentationEditor->document($class);
$document = $document->toArray();

$channel = $this->channelRenderer->render($document);
$message = $this->messageRenderer->render($document);

$channelKey = key($channel);
$messageKey = key($message);

$channels[$channelKey] = $channel[$channelKey];
$messages[$messageKey] = $message[$messageKey];
}

$schema = [
'asyncapi' => $this->schemaVersion,
'info' => $this->infoRenderer->render(),
'channels' => $channels,
'components' => [
'messages' => $messages,
],
];

$servers = $this->serverRenderer->render();

if ($servers) {
$schema['servers'] = $servers;
}

return $schema;
}
}
27 changes: 27 additions & 0 deletions src/Schema/V3/ServerRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Ferror\AsyncapiDocBundle\Schema\V3;

final readonly class ServerRenderer
{
public function __construct(private array $servers)
{
}

public function render(): array
{
$servers = [];

foreach ($this->servers as $name => $properties) {
$url = $properties['url'];
unset($properties['url']);
$properties['host'] = $url;

$servers[$name] = $properties;
}

return $servers;
}
}
1 change: 0 additions & 1 deletion src/Symfony/Console/DumpSpecificationConsole.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Ferror\AsyncapiDocBundle\Symfony\Console;


use Ferror\AsyncapiDocBundle\DataFormat;
use Ferror\AsyncapiDocBundle\DocumentationStrategy\DocumentationStrategyInterface;
use Ferror\AsyncapiDocBundle\Generator\GeneratorFactory;
Expand Down
Loading

0 comments on commit 5eef556

Please sign in to comment.