Skip to content

Commit

Permalink
feat: reference formats
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Feb 10, 2025
1 parent ad54075 commit 981e6e1
Show file tree
Hide file tree
Showing 20 changed files with 475 additions and 217 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -425,16 +425,15 @@ jobs:
- name: Export OpenAPI documents
run: |
mkdir -p build/out/openapi
tests/Fixtures/app/console api:openapi:export -o build/out/openapi/openapi_v3.json
# tests/Fixtures/app/console api:openapi:export -o build/out/openapi/openapi_v3.json
tests/Fixtures/app/console api:openapi:export --yaml -o build/out/openapi/openapi_v3.yaml
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '14'
- name: Validate OpenAPI documents
run: |
npx swagger-cli validate build/out/openapi/openapi_v3.json
npx swagger-cli validate build/out/openapi/openapi_v3.yaml
npx @quobix/vacuum lint validate build/out/openapi/openapi_v3.yaml
- name: Upload OpenAPI artifacts
if: always()
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -1227,16 +1226,15 @@ jobs:
- name: Export OpenAPI documents
run: |
mkdir -p build/out/openapi
tests/Fixtures/app/console api:openapi:export -o build/out/openapi/openapi_v3.json
# tests/Fixtures/app/console api:openapi:export -o build/out/openapi/openapi_v3.json
tests/Fixtures/app/console api:openapi:export --yaml -o build/out/openapi/openapi_v3.yaml
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: '14'
- name: Validate OpenAPI documents
run: |
npx swagger-cli validate build/out/openapi/openapi_v3.json
npx swagger-cli validate build/out/openapi/openapi_v3.yaml
npx @quobix/vacuum lint validate build/out/openapi/openapi_v3.yaml
- name: Upload OpenAPI artifacts
if: always()
uses: actions/upload-artifact@v4
Expand Down
169 changes: 117 additions & 52 deletions src/Hal/JsonSchema/SchemaFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@

namespace ApiPlatform\Hal\JsonSchema;

use ApiPlatform\JsonSchema\DefinitionNameFactory;
use ApiPlatform\JsonSchema\DefinitionNameFactoryInterface;
use ApiPlatform\JsonSchema\ResourceMetadataTrait;
use ApiPlatform\JsonSchema\Schema;
use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface;
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
use ApiPlatform\JsonSchema\SchemaUriPrefixTrait;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;

/**
* Decorator factory which adds HAL properties to the JSON Schema document.
Expand All @@ -26,6 +31,11 @@
*/
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
{
use ResourceMetadataTrait;
use SchemaUriPrefixTrait;

private const COLLECTION_BASE_SCHEMA_NAME = 'HalCollectionBaseSchema';

private const HREF_PROP = [
'href' => [
'type' => 'string',
Expand All @@ -44,8 +54,12 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
],
];

public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
public function __construct(private readonly SchemaFactoryInterface $schemaFactory, private ?DefinitionNameFactoryInterface $definitionNameFactory = null, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null)

Check warning on line 57 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L57

Added line #L57 was not covered by tests
{
if (!$definitionNameFactory) {
$this->definitionNameFactory = new DefinitionNameFactory();

Check warning on line 60 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L59-L60

Added lines #L59 - L60 were not covered by tests
}
$this->resourceMetadataFactory = $resourceMetadataFactory;

Check warning on line 62 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L62

Added line #L62 was not covered by tests
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
$this->schemaFactory->setSchemaFactory($this);
}
Expand All @@ -56,79 +70,130 @@ public function __construct(private readonly SchemaFactoryInterface $schemaFacto
*/
public function buildSchema(string $className, string $format = 'jsonhal', string $type = Schema::TYPE_OUTPUT, ?Operation $operation = null, ?Schema $schema = null, ?array $serializerContext = null, bool $forceCollection = false): Schema
{
$schema = $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
if ('jsonhal' !== $format) {
return $schema;
return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);

Check warning on line 74 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L74

Added line #L74 was not covered by tests
}

if (!$this->isResourceClass($className)) {
$operation = null;
$serializerContext ??= [];

Check warning on line 79 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L77-L79

Added lines #L77 - L79 were not covered by tests
} else {
$operation = $this->findOperation($className, $type, $operation, $serializerContext, $format);
$inputOrOutputClass = $this->findOutputClass($className, $type, $operation, $serializerContext);
$serializerContext ??= $this->getSerializerContext($operation, $type);

Check warning on line 83 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L81-L83

Added lines #L81 - L83 were not covered by tests
}

if (null === $inputOrOutputClass) {

Check failure on line 86 in src/Hal/JsonSchema/SchemaFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.4)

Variable $inputOrOutputClass might not be defined.

Check warning on line 86 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L86

Added line #L86 was not covered by tests
// input or output disabled
return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);

Check warning on line 88 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L88

Added line #L88 was not covered by tests
}

$schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection);

Check warning on line 91 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L91

Added line #L91 was not covered by tests
$definitions = $schema->getDefinitions();
if ($key = $schema->getRootDefinitionKey()) {
$definitions[$key]['properties'] = self::BASE_PROPS + ($definitions[$key]['properties'] ?? []);
$definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext ?? []);

Check failure on line 93 in src/Hal/JsonSchema/SchemaFactory.php

View workflow job for this annotation

GitHub Actions / PHPStan (PHP 8.4)

Variable $serializerContext on left side of ?? always exists and is not nullable.
$prefix = $this->getSchemaUriPrefix($schema->getVersion());
$collectionKey = $schema->getItemsDefinitionKey();

Check warning on line 95 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L93-L95

Added lines #L93 - L95 were not covered by tests

// Already computed
if (!$collectionKey && isset($definitions[$definitionName])) {
$schema['$ref'] = $prefix.$definitionName;

Check warning on line 99 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L98-L99

Added lines #L98 - L99 were not covered by tests

return $schema;
}
if ($key = $schema->getItemsDefinitionKey()) {
$definitions[$key]['properties'] = self::BASE_PROPS + ($definitions[$key]['properties'] ?? []);

$key = $schema->getRootDefinitionKey() ?? $collectionKey;

Check warning on line 104 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L104

Added line #L104 was not covered by tests

$definitions[$definitionName] = [
'allOf' => [
['type' => 'object', 'properties' => self::BASE_PROPS],
['$ref' => $prefix.$key],
],
];

Check warning on line 111 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L106-L111

Added lines #L106 - L111 were not covered by tests

if (isset($definitions[$key]['description'])) {
$definitions[$definitionName]['description'] = $definitions[$key]['description'];

Check warning on line 114 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L113-L114

Added lines #L113 - L114 were not covered by tests
}

if (!$collectionKey) {
$schema['$ref'] = $prefix.$definitionName;

Check warning on line 118 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L117-L118

Added lines #L117 - L118 were not covered by tests

return $schema;

Check warning on line 120 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L120

Added line #L120 was not covered by tests
}

if (($schema['type'] ?? '') === 'array') {
$items = $schema['items'];
unset($schema['items']);

$schema['type'] = 'object';
$schema['properties'] = [
'_embedded' => [
'anyOf' => [
[
if (!isset($definitions[self::COLLECTION_BASE_SCHEMA_NAME])) {
$definitions[self::COLLECTION_BASE_SCHEMA_NAME] = [
'type' => 'object',
'properties' => [
'_embedded' => [
'anyOf' => [
[
'type' => 'object',
'properties' => [
'item' => [
'type' => 'array',
],
],
],
['type' => 'object'],
],
],
'totalItems' => [
'type' => 'integer',
'minimum' => 0,
],
'itemsPerPage' => [
'type' => 'integer',
'minimum' => 0,
],
'_links' => [

Check warning on line 149 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L124-L149

Added lines #L124 - L149 were not covered by tests
'type' => 'object',
'properties' => [
'item' => [
'type' => 'array',
'items' => $items,
'self' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'first' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'last' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'next' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'previous' => [
'type' => 'object',
'properties' => self::HREF_PROP,

Check warning on line 170 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L152-L170

Added lines #L152 - L170 were not covered by tests
],
],
],
['type' => 'object'],
],
],
'totalItems' => [
'type' => 'integer',
'minimum' => 0,
],
'itemsPerPage' => [
'type' => 'integer',
'minimum' => 0,
],
'_links' => [
'required' => ['_links', '_embedded'],
];

Check warning on line 176 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L175-L176

Added lines #L175 - L176 were not covered by tests
}

unset($schema['items']);
unset($schema['type']);

Check warning on line 180 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L179-L180

Added lines #L179 - L180 were not covered by tests

$schema['description'] = "$definitionName collection.";
$schema['allOf'] = [
['$ref' => $prefix.self::COLLECTION_BASE_SCHEMA_NAME],
[

Check warning on line 185 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L182-L185

Added lines #L182 - L185 were not covered by tests
'type' => 'object',
'properties' => [
'self' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'first' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'last' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'next' => [
'type' => 'object',
'properties' => self::HREF_PROP,
],
'previous' => [
'type' => 'object',
'properties' => self::HREF_PROP,
'_embedded' => [
'additionalProperties' => [
'type' => 'array',
'items' => ['$ref' => $prefix.$definitionName],
],

Check warning on line 192 in src/Hal/JsonSchema/SchemaFactory.php

View check run for this annotation

Codecov / codecov/patch

src/Hal/JsonSchema/SchemaFactory.php#L188-L192

Added lines #L188 - L192 were not covered by tests
],
],
],
];
$schema['required'] = [
'_links',
'_embedded',
];

return $schema;
}
Expand Down
Loading

0 comments on commit 981e6e1

Please sign in to comment.