Skip to content

Commit 2a3c385

Browse files
committed
nice
1 parent 7cc3f8c commit 2a3c385

File tree

7 files changed

+160
-33
lines changed

7 files changed

+160
-33
lines changed

src/Hydra/JsonSchema/SchemaFactory.php

+42-16
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,28 @@ public function buildSchema(string $className, string $format = 'jsonld', string
118118
return $this->schemaFactory->buildSchema($className, $format, $type, $operation, $schema, $serializerContext, $forceCollection);
119119
}
120120

121-
$schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection);
122-
$schema = $this->schemaFactory->buildSchema($className, 'jsonld', $type, $operation, $schema, [self::COMPUTE_REFERENCES => true] + $serializerContext, $forceCollection);
121+
if ($schema) {
122+
$definitions = $schema->getDefinitions();
123+
$jsonDefinitionName = $this->definitionNameFactory->create($className, 'json', $className, $operation, $serializerContext);
124+
125+
if (!isset($definitions[$jsonDefinitionName])) {
126+
$schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection);
127+
}
128+
} else {
129+
$schema = $this->schemaFactory->buildSchema($className, 'json', $type, $operation, $schema, $serializerContext, $forceCollection);
130+
}
131+
123132
$definitionName = $this->definitionNameFactory->create($className, $format, $className, $operation, $serializerContext);
124133
$definitions = $schema->getDefinitions();
134+
135+
$addJsonLdBaseSchema = false;
136+
137+
if (!isset($definitions[$definitionName])) {
138+
$addJsonLdBaseSchema = true;
139+
// only compute json-ld references, skip the scalar properties as they're inherited from the json format
140+
$schema = $this->schemaFactory->buildSchema($className, 'jsonld', $type, $operation, $schema, [self::COMPUTE_REFERENCES => true] + $serializerContext, $forceCollection);
141+
}
142+
125143
$prefix = $this->getSchemaUriPrefix($schema->getVersion());
126144
$collectionKey = $schema->getItemsDefinitionKey();
127145

@@ -131,29 +149,37 @@ public function buildSchema(string $className, string $format = 'jsonld', string
131149
$definitions[$name] = Schema::TYPE_OUTPUT === $type ? self::ITEM_BASE_SCHEMA_OUTPUT : self::ITEM_BASE_SCHEMA;
132150
}
133151

134-
if (!$collectionKey) {
135-
$schema['definitions'][$definitionName] = [
136-
'allOf' => [
137-
['$ref' => $prefix.$name],
138-
['$ref' => $prefix.$key],
139-
$definitions[$definitionName],
140-
],
152+
if (!$collectionKey && isset($definitions[$definitionName])) {
153+
if (!$addJsonLdBaseSchema) {
154+
$schema['$ref'] = $prefix.$definitionName;
155+
156+
return $schema;
157+
}
158+
159+
$allOf = [
160+
['$ref' => $prefix.$name],
161+
['$ref' => $prefix.$key],
141162
];
142-
$schema['$ref'] = $preifx . $definitionName;
143163

144-
return $schema;
145-
}
164+
// if there're no properties, we did not compute any json-ld specific reference
165+
if (isset($definitions[$definitionName]['properties'])) {
166+
$allOf[] = $definitions[$definitionName];
167+
}
146168

147-
if (isset($definitions[$key]['description'])) {
148-
$definitions[$definitionName]['description'] = $definitions[$key]['description'];
149-
}
169+
$definitions[$definitionName] = new \ArrayObject([
170+
'allOf' => $allOf,
171+
]);
150172

151-
if (!$collectionKey) {
173+
$schema->setDefinitions($definitions);
152174
$schema['$ref'] = $prefix.$definitionName;
153175

154176
return $schema;
155177
}
156178

179+
if (isset($definitions[$key]['description'])) {
180+
$definitions[$definitionName]['description'] = $definitions[$key]['description'];
181+
}
182+
157183
// handle hydra:Collection
158184
if (($schema['type'] ?? '') === 'array') {
159185
$hydraPrefix = $this->getHydraPrefix($serializerContext + $this->defaultContext);

src/JsonSchema/SchemaFactory.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
165165
$extraProperties = $propertyMetadata->getExtraProperties() ?? [];
166166
// see AttributePropertyMetadataFactory
167167
if (true === ($extraProperties[SchemaPropertyMetadataFactory::JSON_SCHEMA_USER_DEFINED] ?? false)) {
168-
if (true === $serializerContext[self::COMPUTE_REFERENCES] ?? null) {
168+
if (true === ($serializerContext[self::COMPUTE_REFERENCES] ?? null)) {
169169
return;
170170

171171
}
@@ -195,7 +195,7 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
195195
|| ($propertySchema['format'] ?? $propertySchema['enum'] ?? false)
196196
)
197197
) {
198-
if (true === $serializerContext[self::COMPUTE_REFERENCES]) {
198+
if (true === ($serializerContext[self::COMPUTE_REFERENCES] ?? null)) {
199199
return;
200200
}
201201

@@ -238,13 +238,13 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
238238
continue;
239239
}
240240

241-
if (false === $propertyMetadata->getGenId()) {
242-
$subDefinitionName = $this->definitionNameFactory->create($className, $format, $className, null, $serializerContext);
243-
244-
if (isset($subSchema->getDefinitions()[$subDefinitionName])) {
245-
unset($subSchema->getDefinitions()[$subDefinitionName]['properties']['@id']);
246-
}
247-
}
241+
// if (false === $propertyMetadata->getGenId()) {
242+
// $subDefinitionName = $this->definitionNameFactory->create($className, $format, $className, null, $serializerContext);
243+
//
244+
// if (isset($subSchema->getDefinitions()[$subDefinitionName])) {
245+
// unset($subSchema->getDefinitions()[$subDefinitionName]['properties']['@id']);
246+
// }
247+
// }
248248

249249
if ($isCollection) {
250250
$key = ($propertySchema['type'] ?? null) === 'object' ? 'additionalProperties' : 'items';
@@ -261,7 +261,7 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
261261
$refs[] = ['type' => 'null'];
262262
}
263263

264-
if (!$hasClassName && (true === $serializerContext[self::COMPUTE_REFERENCES] ?? null)) {
264+
if (!$hasClassName && true === ($serializerContext[self::COMPUTE_REFERENCES] ?? null)) {
265265
return;
266266
}
267267

src/OpenApi/Factory/OpenApiFactory.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@
5555
use ApiPlatform\OpenApi\Serializer\NormalizeOperationNameTrait;
5656
use ApiPlatform\State\ApiResource\Error as ApiResourceError;
5757
use ApiPlatform\State\Pagination\PaginationOptions;
58+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5793\BagOfTests;
59+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5793\TestEntity;
5860
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\PropertyCollectionIriOnly;
61+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\WritableId;
5962
use ApiPlatform\Validator\Exception\ValidationException;
6063
use Psr\Container\ContainerInterface;
6164
use Symfony\Component\PropertyInfo\Type;
@@ -122,8 +125,8 @@ public function __invoke(array $context = []): OpenApi
122125
$webhooks = new \ArrayObject();
123126
$tags = [];
124127

125-
foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
126-
// foreach ([PropertyCollectionIriOnly::class] as $resourceClass) {
128+
// foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
129+
foreach ([WritableId::class] as $resourceClass) {
127130
$resourceMetadataCollection = $this->resourceMetadataFactory->create($resourceClass);
128131
foreach ($resourceMetadataCollection as $resourceMetadata) {
129132
$this->collectPaths($resourceMetadata, $resourceMetadataCollection, $paths, $schemas, $webhooks, $tags, $context);

tests/Fixtures/TestBundle/Entity/Issue5793/BagOfTests.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class BagOfTests
4949
#[Groups(['read', 'write'])]
5050
private Collection $nonResourceTests;
5151

52-
#[ORM\ManyToOne(targetEntity: TestEntity::class)]
52+
#[ORM\ManyToOne(targetEntity: TestEntity::class, cascade: ['persist'])]
5353
#[ORM\JoinColumn(name: 'type', referencedColumnName: 'id', nullable: false)]
5454
#[Groups(['read', 'write'])]
5555
protected ?TestEntity $type = null;

tests/Fixtures/app/AppKernel.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ public function registerBundles(): array
8080
$bundles[] = new FriendsOfBehatSymfonyExtensionBundle();
8181
}
8282

83-
if (class_exists(DoctrineMongoDBBundle::class)) {
84-
$bundles[] = new DoctrineMongoDBBundle();
85-
}
83+
// if (class_exists(DoctrineMongoDBBundle::class)) {
84+
// $bundles[] = new DoctrineMongoDBBundle();
85+
// }
8686

8787
$bundles[] = new TestBundle();
8888

tests/Functional/SchemaTest.php

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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\Tests\Functional;
15+
16+
use ApiPlatform\JsonSchema\Schema;
17+
use ApiPlatform\JsonSchema\SchemaFactoryInterface;
18+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
19+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5793\BagOfTests;
20+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue5793\TestEntity;
21+
use ApiPlatform\Tests\SetupClassResourcesTrait;
22+
use JsonSchema\Validator;
23+
use PHPUnit\Framework\Attributes\DataProvider;
24+
25+
class SchemaTest extends ApiTestCase
26+
{
27+
use SetupClassResourcesTrait;
28+
private static ?SchemaFactoryInterface $schemaFactory = null;
29+
30+
/**
31+
* @return class-string[]
32+
*/
33+
public static function getResources(): array
34+
{
35+
return [BagOfTests::class, TestEntity::class];
36+
}
37+
38+
private static function getSchemaFactory(): SchemaFactoryInterface
39+
{
40+
if (static::$schemaFactory) {
41+
return static::$schemaFactory;
42+
}
43+
44+
$container = static::getContainer();
45+
/** @var SchemaFactoryInterface $schemaFactory */
46+
$schemaFactory = $container->get('api_platform.json_schema.schema_factory');
47+
48+
return static::$schemaFactory = $schemaFactory;
49+
}
50+
51+
#[DataProvider('getInvalidSchemas')]
52+
public function testSchemaIsNotValid(string $json, Schema $schema): void
53+
{
54+
$validator = new Validator();
55+
$json = json_decode($json, null, 512, \JSON_THROW_ON_ERROR);
56+
$validator->validate($json, $schema->getArrayCopy());
57+
$this->assertFalse($validator->isValid());
58+
}
59+
60+
61+
#[DataProvider('getSchemas')]
62+
public function testSchemaIsValid(string $json, Schema $schema): void
63+
{
64+
$validator = new Validator();
65+
$json = json_decode($json, null, 512, \JSON_THROW_ON_ERROR);
66+
$validator->validate($json, $schema->getArrayCopy());
67+
$this->assertTrue($validator->isValid());
68+
}
69+
70+
/**
71+
* @return array<string, array{string, string}>
72+
*/
73+
public static function getSchemas(): array
74+
{
75+
return [
76+
'json-ld' => [
77+
'{"@context":"/contexts/BagOfTests","@id":"/bag_of_tests/1","@type":"BagOfTests","id":1,"description":"string","tests":"a string","nonResourceTests":[{"id":1,"nullableString":"string","nullableInt":0}],"type":{"@id":"/test_entities/1","@type":"TestEntity","id":1,"nullableString":"string","nullableInt":0}}',
78+
static::getSchemaFactory()->buildSchema(BagOfTests::class, 'jsonld'),
79+
],
80+
'json-ld Collection' => [
81+
'{"@context":"/contexts/BagOfTests","@id":"/bag_of_tests","@type":"hydra:Collection","hydra:totalItems":1,"hydra:member":[{"@id":"/bag_of_tests/1","@type":"BagOfTests","id":1,"description":"string","nonResourceTests":[],"type":{"@id":"/test_entities/1","@type":"TestEntity","id":1,"nullableString":"string","nullableInt":0}}]}',
82+
static::getSchemaFactory()->buildSchema(BagOfTests::class, 'jsonld', forceCollection: true),
83+
]
84+
];
85+
}
86+
87+
/**
88+
* @return array<string, array{string, string}>
89+
*/
90+
public static function getInvalidSchemas(): array
91+
{
92+
return [
93+
'json-ld' => [
94+
'{"@context":"/contexts/BagOfTests","@id":"/bag_of_tests/1","@type":"BagOfTests","id":1,"description":"string","tests":"a string","nonResourceTests":[{"id":1,"nullableString":"string","nullableInt":0}],"type":{"@type":"TestEntity","id":1,"nullableString":"string","nullableInt":0}}',
95+
static::getSchemaFactory()->buildSchema(BagOfTests::class, 'jsonld'),
96+
],
97+
];
98+
}
99+
}

tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php

-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ public function testSubSchemaJsonLd(): void
251251
{
252252
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => RelatedDummy::class, '--type' => 'output', '--format' => 'jsonld']);
253253
$result = $this->tester->getDisplay();
254-
dd('ok');
255254
$json = json_decode($result, associative: true);
256255

257256
$this->assertArrayHasKey('@id', $json['definitions']['ThirdLevel.jsonld-friends']['properties']);

0 commit comments

Comments
 (0)