Skip to content

Commit c555d4e

Browse files
fix(jsonschema): generation of non-LD+JSON distinct schema formats
1 parent 5a2600f commit c555d4e

File tree

12 files changed

+57
-19
lines changed

12 files changed

+57
-19
lines changed

src/Hal/JsonSchema/SchemaFactory.php

-4
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ final class SchemaFactory implements SchemaFactoryInterface
4646

4747
public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
4848
{
49-
$this->addDistinctFormat('jsonhal');
5049
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
5150
$this->schemaFactory->setSchemaFactory($this);
5251
}
@@ -129,8 +128,5 @@ public function buildSchema(string $className, string $format = 'jsonhal', strin
129128

130129
public function addDistinctFormat(string $format): void
131130
{
132-
if (method_exists($this->schemaFactory, 'addDistinctFormat')) {
133-
$this->schemaFactory->addDistinctFormat($format);
134-
}
135131
}
136132
}

src/Hydra/JsonSchema/SchemaFactory.php

-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
use ApiPlatform\JsonLd\ContextBuilder;
1717
use ApiPlatform\JsonSchema\Schema;
18-
use ApiPlatform\JsonSchema\SchemaFactory as BaseSchemaFactory;
1918
use ApiPlatform\JsonSchema\SchemaFactoryAwareInterface;
2019
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
2120
use ApiPlatform\Metadata\Operation;
@@ -60,7 +59,6 @@ final class SchemaFactory implements SchemaFactoryInterface
6059

6160
public function __construct(private readonly SchemaFactoryInterface $schemaFactory)
6261
{
63-
$this->addDistinctFormat('jsonld');
6462
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
6563
$this->schemaFactory->setSchemaFactory($this);
6664
}
@@ -186,8 +184,5 @@ public function buildSchema(string $className, string $format = 'jsonld', string
186184

187185
public function addDistinctFormat(string $format): void
188186
{
189-
if ($this->schemaFactory instanceof BaseSchemaFactory) {
190-
$this->schemaFactory->addDistinctFormat($format);
191-
}
192187
}
193188
}

src/JsonSchema/SchemaFactory.php

+2-6
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,13 @@
3636
final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareInterface
3737
{
3838
use ResourceClassInfoTrait;
39-
private array $distinctFormats = [];
4039
private ?TypeFactoryInterface $typeFactory = null;
4140
private ?SchemaFactoryInterface $schemaFactory = null;
4241
// Edge case where the related resource is not readable (for example: NotExposed) but we have groups to read the whole related object
4342
public const FORCE_SUBSCHEMA = '_api_subschema_force_readable_link';
4443
public const OPENAPI_DEFINITION_NAME = 'openapi_definition_name';
4544

46-
public function __construct(?TypeFactoryInterface $typeFactory, ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ?NameConverterInterface $nameConverter = null, ?ResourceClassResolverInterface $resourceClassResolver = null)
45+
public function __construct(?TypeFactoryInterface $typeFactory, ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ?NameConverterInterface $nameConverter = null, ?ResourceClassResolverInterface $resourceClassResolver = null, private readonly ?array $distinctFormats = null)
4746
{
4847
if ($typeFactory) {
4948
$this->typeFactory = $typeFactory;
@@ -54,13 +53,10 @@ public function __construct(?TypeFactoryInterface $typeFactory, ResourceMetadata
5453
}
5554

5655
/**
57-
* When added to the list, the given format will lead to the creation of a new definition.
58-
*
5956
* @internal
6057
*/
6158
public function addDistinctFormat(string $format): void
6259
{
63-
$this->distinctFormats[$format] = true;
6460
}
6561

6662
/**
@@ -267,7 +263,7 @@ private function buildDefinitionName(string $className, string $format = 'json',
267263
$prefix .= '.'.$shortName;
268264
}
269265

270-
if (isset($this->distinctFormats[$format])) {
266+
if ('json' !== $format && isset($this->distinctFormats[$format])) {
271267
// JSON is the default, and so isn't included in the definition name
272268
$prefix .= '.'.$format;
273269
}

src/Symfony/Bundle/ApiPlatformBundle.php

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlResolverPass;
2424
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlTypePass;
2525
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MetadataAwareNameConverterPass;
26+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\SchemaFactoryPass;
2627
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestClientPass;
2728
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestMercureHubPass;
2829
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
@@ -58,5 +59,6 @@ public function build(ContainerBuilder $container): void
5859
$container->addCompilerPass(new TestClientPass());
5960
$container->addCompilerPass(new TestMercureHubPass());
6061
$container->addCompilerPass(new AuthenticatorManagerPass());
62+
$container->addCompilerPass(new SchemaFactoryPass());
6163
}
6264
}

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public function load(array $configs, ContainerBuilder $container): void
120120
$patchFormats = $this->getFormats($config['patch_formats']);
121121
$errorFormats = $this->getFormats($config['error_formats']);
122122
$docsFormats = $this->getFormats($config['docs_formats']);
123+
$jsonSchemaFormats = $config['jsonschema_formats'] ? $this->getFormats($config['jsonschema_formats']) : $formats;
123124

124125
if (!isset($errorFormats['json'])) {
125126
$errorFormats['json'] = ['application/problem+json', 'application/json'];
@@ -144,7 +145,7 @@ public function load(array $configs, ContainerBuilder $container): void
144145
$docsFormats['jsonopenapi'] = ['application/vnd.openapi+json'];
145146
}
146147

147-
$this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats, $docsFormats);
148+
$this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats, $docsFormats, $jsonSchemaFormats);
148149
$this->registerMetadataConfiguration($container, $config, $loader);
149150
$this->registerOAuthConfiguration($container, $config);
150151
$this->registerOpenApiConfiguration($container, $config, $loader);
@@ -185,7 +186,7 @@ public function load(array $configs, ContainerBuilder $container): void
185186
$this->registerInflectorConfiguration($config);
186187
}
187188

188-
private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats, array $docsFormats): void
189+
private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats, array $docsFormats, array $jsonSchemaFormats): void
189190
{
190191
$loader->load('symfony/events.xml');
191192
$loader->load('symfony/controller.xml');
@@ -218,6 +219,7 @@ private function registerCommonConfiguration(ContainerBuilder $container, array
218219
$container->setParameter('api_platform.patch_formats', $patchFormats);
219220
$container->setParameter('api_platform.error_formats', $errorFormats);
220221
$container->setParameter('api_platform.docs_formats', $docsFormats);
222+
$container->setParameter('api_platform.jsonschema_formats', $jsonSchemaFormats);
221223
$container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
222224
$container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
223225
$container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler;
15+
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
19+
/**
20+
* Inject in-use JSON Schema format into the factory.
21+
*
22+
* @internal
23+
*/
24+
final class SchemaFactoryPass implements CompilerPassInterface
25+
{
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function process(ContainerBuilder $container): void
30+
{
31+
if ($container->hasDefinition('api_platform.json_schema.schema_factory')) {
32+
$container->getDefinition('api_platform.json_schema.schema_factory')->setArgument(6, '%api_platform.jsonschema_formats%');
33+
}
34+
}
35+
}

src/Symfony/Bundle/DependencyInjection/Configuration.php

+2
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ public function getConfigTreeBuilder(): TreeBuilder
174174
'jsonproblem' => ['mime_types' => ['application/problem+json']],
175175
'json' => ['mime_types' => ['application/problem+json', 'application/json']],
176176
]);
177+
$this->addFormatSection($rootNode, 'jsonschema_formats', [
178+
]);
177179

178180
$this->addDefaultsSection($rootNode);
179181

src/Symfony/Bundle/Resources/config/json_schema.xml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<argument type="service" id="api_platform.metadata.property.metadata_factory" />
2323
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
2424
<argument type="service" id="api_platform.resource_class_resolver" />
25+
<argument type="collection" />
2526
</service>
2627
<service id="ApiPlatform\JsonSchema\SchemaFactoryInterface" alias="api_platform.json_schema.schema_factory" />
2728

tests/Hal/JsonSchema/SchemaFactoryTest.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ protected function setUp(): void
5353
null,
5454
$resourceMetadataFactory->reveal(),
5555
$propertyNameCollectionFactory->reveal(),
56-
$propertyMetadataFactory->reveal()
56+
$propertyMetadataFactory->reveal(),
57+
null,
58+
null,
59+
['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true],
5760
);
5861

5962
$hydraSchemaFactory = new HydraSchemaFactory($baseSchemaFactory);

tests/Hydra/JsonSchema/SchemaFactoryTest.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ protected function setUp(): void
5454
null,
5555
$resourceMetadataFactoryCollection->reveal(),
5656
$propertyNameCollectionFactory->reveal(),
57-
$propertyMetadataFactory->reveal()
57+
$propertyMetadataFactory->reveal(),
58+
null,
59+
null,
60+
['jsonapi' => true, 'jsonhal' => true, 'jsonld' => true],
5861
);
5962

6063
$this->schemaFactory = new SchemaFactory($baseSchemaFactory);

tests/Symfony/Bundle/ApiPlatformBundleTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlResolverPass;
2525
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlTypePass;
2626
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MetadataAwareNameConverterPass;
27+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\SchemaFactoryPass;
2728
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestClientPass;
2829
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\TestMercureHubPass;
2930
use PHPUnit\Framework\TestCase;
@@ -54,6 +55,7 @@ public function testBuild(): void
5455
$containerProphecy->addCompilerPass(Argument::type(TestClientPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5556
$containerProphecy->addCompilerPass(Argument::type(TestMercureHubPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5657
$containerProphecy->addCompilerPass(Argument::type(AuthenticatorManagerPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
58+
$containerProphecy->addCompilerPass(Argument::type(SchemaFactoryPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5759

5860
$bundle = new ApiPlatformBundle();
5961
$bundle->build($containerProphecy->reveal());

tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
9898
'jsonld' => ['mime_types' => ['application/ld+json']],
9999
'json' => ['mime_types' => ['application/problem+json', 'application/json']],
100100
],
101+
'jsonschema_formats' => [],
101102
'exception_to_status' => [
102103
ExceptionInterface::class => Response::HTTP_BAD_REQUEST,
103104
InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,

0 commit comments

Comments
 (0)