Skip to content

Commit 651e978

Browse files
committed
okay
1 parent 88ff9fb commit 651e978

File tree

8 files changed

+60
-32
lines changed

8 files changed

+60
-32
lines changed

‎src/JsonSchema/Metadata/Property/Factory/SchemaPropertyMetadataFactory.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,11 @@ private function getTypeSchema(ApiProperty $propertyMetadata, array $propertySch
127127
$propertySchema['example'] = $example;
128128
}
129129

130+
$hasType = $this->getSchemaValue($propertySchema, 'type') || $this->getSchemaValue($propertyMetadata->getJsonSchemaContext() ?? [], 'type') || $this->getSchemaValue($propertyMetadata->getOpenapiContext() ?? [], 'type');
131+
$hasRef = $this->getSchemaValue($propertySchema, '$ref') || $this->getSchemaValue($propertyMetadata->getJsonSchemaContext() ?? [], '$ref') || $this->getSchemaValue($propertyMetadata->getOpenapiContext() ?? [], '$ref');
132+
130133
// never override the following keys if at least one is already set or if there's a custom openapi context
131-
if (
132-
null === $type
133-
|| ($propertySchema['type'] ?? $propertySchema['$ref'] ?? $propertySchema['anyOf'] ?? $propertySchema['allOf'] ?? $propertySchema['oneOf'] ?? false)
134-
|| \array_key_exists('type', $propertyMetadata->getOpenapiContext() ?? [])
135-
) {
134+
if ($hasType || $hasRef || !$type) {
136135
return $propertySchema;
137136
}
138137

@@ -559,4 +558,13 @@ private function addNullabilityToTypeDefinition(array $jsonSchema, LegacyType $t
559558
: [$jsonSchema['type'], 'null'],
560559
]];
561560
}
561+
562+
private function getSchemaValue(array $schema, string $key): array|string|null
563+
{
564+
if (isset($schema['items'])) {
565+
$schema = $schema['items'];
566+
}
567+
568+
return $schema[$key] ?? $schema['allOf'][0][$key] ?? $schema['anyOf'][0][$key] ?? $schema['oneOf'][0][$key] ?? null;
569+
}
562570
}

‎src/JsonSchema/SchemaFactory.php

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,6 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
291291
$additionalPropertySchema ?? []
292292
);
293293

294-
// @see https://github.com/api-platform/core/issues/6299
295-
if (Schema::UNKNOWN_TYPE === ($propertySchema['type'] ?? null) && isset($propertySchema['$ref'])) {
296-
unset($propertySchema['type']);
297-
}
298-
299294
$extraProperties = $propertyMetadata->getExtraProperties() ?? [];
300295
// see AttributePropertyMetadataFactory
301296
if (true === ($extraProperties[SchemaPropertyMetadataFactory::JSON_SCHEMA_USER_DEFINED] ?? false)) {
@@ -306,24 +301,29 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
306301
}
307302

308303
$type = $propertyMetadata->getNativeType();
309-
$propertySchemaType = $propertySchema['type'] ?? Schema::UNKNOWN_TYPE;
310-
$isSchemaDefined = ($propertySchema['$ref'] ?? $propertySchema['anyOf'] ?? $propertySchema['allOf'] ?? $propertySchema['oneOf'] ?? false)
311-
|| ($propertySchemaType && 'string' !== $propertySchemaType && !(\is_array($propertySchemaType) && !\in_array('string', $propertySchemaType, true)))
312-
|| (($propertySchema['format'] ?? $propertySchema['enum'] ?? false) && $propertySchemaType);
304+
305+
// Type is defined in an allOf, anyOf, or oneOf
306+
$propertySchemaType = $this->getSchemaValue($propertySchema, 'type');
307+
308+
$currentRef = $this->getSchemaValue($propertySchema, '$ref');
309+
$isSchemaDefined = null !== ($currentRef ?? $this->getSchemaValue($propertySchema, 'format') ?? $this->getSchemaValue($propertySchema, 'enum'));
310+
if (!$isSchemaDefined && Schema::UNKNOWN_TYPE !== $propertySchemaType) {
311+
$isSchemaDefined = true;
312+
}
313313

314314
// Check if the type is considered "unknown" by SchemaPropertyMetadataFactory
315-
$isUnknown = Schema::UNKNOWN_TYPE === $propertySchemaType
316-
|| ('array' === $propertySchemaType && Schema::UNKNOWN_TYPE === ($propertySchema['items']['type'] ?? null))
317-
|| ('object' === $propertySchemaType && Schema::UNKNOWN_TYPE === ($propertySchema['additionalProperties']['type'] ?? null));
315+
if (isset($propertySchema['additionalProperties']['type']) && Schema::UNKNOWN_TYPE === $propertySchema['additionalProperties']['type']) {
316+
$isSchemaDefined = false;
317+
}
318318

319-
// If schema is defined and not marked as unknown, or if no type info exists, return early
320-
if (!$isUnknown && (null === $type || $isSchemaDefined)) {
319+
if ($isSchemaDefined && Schema::UNKNOWN_TYPE !== $propertySchemaType) {
320+
// If schema is defined and not marked as unknown, or if no type info exists, return early
321321
$schema->getDefinitions()[$definitionName]['properties'][$normalizedPropertyName] = new \ArrayObject($propertySchema);
322322

323323
return;
324324
}
325325

326-
if ($isUnknown) {
326+
if (Schema::UNKNOWN_TYPE === $propertySchemaType) {
327327
$propertySchema = [];
328328
}
329329

@@ -332,6 +332,7 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
332332
$refs = [];
333333
$isNullable = $type?->isNullable() ?? false;
334334

335+
// TODO: refactor this with TypeInfo we shouldn't have to loop like this, the below code handles object refs
335336
if ($type) {
336337
foreach ($type instanceof CompositeTypeInterface ? $type->getTypes() : [$type] as $t) {
337338
if ($t instanceof BuiltinType && TypeIdentifier::NULL === $t->getTypeIdentifier()) {
@@ -367,11 +368,14 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
367368

368369
if ($isCollection) {
369370
$key = ($propertySchema['type'] ?? null) === 'object' ? 'additionalProperties' : 'items';
371+
if (!isset($propertySchema['type'])) {
372+
$propertySchema['type'] = 'array';
373+
}
374+
370375
if (!isset($propertySchema[$key]) || !\is_array($propertySchema[$key])) {
371376
$propertySchema[$key] = [];
372377
}
373-
$propertySchema[$key]['$ref'] = $subSchemaResult['$ref'];
374-
unset($propertySchema[$key]['type']);
378+
$propertySchema[$key] = ['$ref' => $subSchemaResult['$ref']];
375379
$refs = [];
376380
break;
377381
}
@@ -386,11 +390,9 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
386390
}
387391

388392
if (($c = \count($refs)) > 1) {
389-
$propertySchema['anyOf'] = $refs;
390-
unset($propertySchema['type'], $propertySchema['$ref']);
393+
$propertySchema = ['anyOf' => $refs];
391394
} elseif (1 === $c) {
392-
$propertySchema['$ref'] = $refs[0]['$ref'];
393-
unset($propertySchema['type']);
395+
$propertySchema = ['$ref' => $refs[0]['$ref']];
394396
}
395397
}
396398

@@ -442,4 +444,13 @@ public function setSchemaFactory(SchemaFactoryInterface $schemaFactory): void
442444
{
443445
$this->schemaFactory = $schemaFactory;
444446
}
447+
448+
private function getSchemaValue(array $schema, string $key): array|string|null
449+
{
450+
if (isset($schema['items'])) {
451+
$schema = $schema['items'];
452+
}
453+
454+
return $schema[$key] ?? $schema['allOf'][0][$key] ?? $schema['anyOf'][0][$key] ?? $schema['oneOf'][0][$key] ?? null;
455+
}
445456
}

‎src/JsonSchema/Tests/SchemaFactoryTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ public function testBuildSchemaForNonResourceClassWithUnionIntersectTypesLegacy(
255255
$this->assertArrayHasKey('ignoredProperty', $definitions[$rootDefinitionKey]['properties']);
256256
$this->assertArrayHasKey('type', $definitions[$rootDefinitionKey]['properties']['ignoredProperty']);
257257
$this->assertSame(['string', 'null'], $definitions[$rootDefinitionKey]['properties']['ignoredProperty']['type']);
258-
259258
$this->assertArrayHasKey('unionType', $definitions[$rootDefinitionKey]['properties']);
260259
$this->assertArrayHasKey('oneOf', $definitions[$rootDefinitionKey]['properties']['unionType']);
261260
$this->assertCount(2, $definitions[$rootDefinitionKey]['properties']['unionType']['oneOf']);

‎tests/Fixtures/TestBundle/ApiResource/ResourceWithEnumProperty.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;
1515

16+
use ApiPlatform\Metadata\ApiProperty;
1617
use ApiPlatform\Metadata\ApiResource;
1718
use ApiPlatform\Metadata\Get;
1819
use ApiPlatform\Metadata\GetCollection;
@@ -30,6 +31,7 @@ class ResourceWithEnumProperty
3031
{
3132
public int $id = 1;
3233

34+
#[ApiProperty(readableLink: true)]
3335
public ?BackedEnumIntegerResource $intEnum = null;
3436

3537
/** @var BackedEnumStringResource[] */

‎tests/Functional/JsonSchema/JsonLdJsonSchemaTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function testSubSchemaJsonLd(): void
5858
'properties' => [
5959
'id' => new \ArrayObject([
6060
'type' => 'integer',
61+
'readOnly' => true,
6162
]),
6263
'description' => new \ArrayObject([
6364
'maxLength' => 255,
@@ -85,6 +86,7 @@ public function testSubSchemaJsonLd(): void
8586
'properties' => [
8687
'id' => new \ArrayObject([
8788
'type' => 'integer',
89+
'readOnly' => true,
8890
]),
8991
'nullableString' => new \ArrayObject([
9092
'type' => [
@@ -111,6 +113,7 @@ public function testSubSchemaJsonLd(): void
111113
'properties' => [
112114
'id' => new \ArrayObject([
113115
'type' => 'integer',
116+
'readOnly' => true,
114117
]),
115118
'nullableString' => new \ArrayObject([
116119
'type' => [

‎tests/Functional/JsonSchema/JsonSchemaTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ public function testResourceWithEnumPropertiesSchema(): void
167167
new \ArrayObject([
168168
'type' => 'array',
169169
'items' => [
170-
'type' => 'string',
171170
'enum' => ['yes', 'no', 'maybe'],
171+
'type' => 'string',
172172
],
173173
]),
174174
$properties['stringEnum']
@@ -184,15 +184,15 @@ public function testResourceWithEnumPropertiesSchema(): void
184184
new \ArrayObject([
185185
'type' => 'array',
186186
'items' => [
187-
'type' => 'string',
188187
'enum' => ['male', 'female'],
188+
'type' => 'string',
189189
],
190190
]),
191191
$properties['genders']
192192
);
193193
}
194194

195-
public function testSchemaWithUnkownType(): void
195+
public function testSchemaWithUnknownType(): void
196196
{
197197
$schema = $this->schemaFactory->buildSchema(Book::class, 'json', Schema::TYPE_OUTPUT, $this->operationMetadataFactory->create('_api_/issue-5452/books{._format}_get_collection'));
198198
$this->assertContains(['$ref' => '#/definitions/ActivableInterface'], $schema['definitions']['Book']['properties']['library']['anyOf']);

‎tests/Functional/OpenApiTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,15 @@ public function testErrorsAreDocumented(): void
6969
'type' => 'object',
7070
'properties' => [
7171
'title' => [
72+
'readOnly' => true,
7273
'description' => 'A short, human-readable summary of the problem.',
7374
'type' => [
7475
0 => 'string',
7576
1 => 'null',
7677
],
7778
],
7879
'detail' => [
80+
'readOnly' => true,
7981
'description' => 'A human-readable explanation specific to this occurrence of the problem.',
8082
'type' => [
8183
0 => 'string',
@@ -90,17 +92,20 @@ public function testErrorsAreDocumented(): void
9092
'default' => 400,
9193
],
9294
'instance' => [
95+
'readOnly' => true,
9396
'description' => 'A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.',
9497
'type' => [
9598
0 => 'string',
9699
1 => 'null',
97100
],
98101
],
99102
'type' => [
103+
'readOnly' => true,
100104
'description' => 'A URI reference that identifies the problem type',
101105
'type' => 'string',
102106
],
103107
'description' => [
108+
'readOnly' => true,
104109
'type' => [
105110
0 => 'string',
106111
1 => 'null',

‎tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ public function testOpenApiResourceRefIsNotOverwritten(): void
142142
$result = $this->tester->getDisplay();
143143
$json = json_decode($result, associative: true);
144144

145-
$this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['itemDto']['$ref']);
146-
$this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['properties']['collectionDto']['items']['$ref']);
145+
$this->assertEquals('#/definitions/DummyFriend', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['allOf'][1]['properties']['itemDto']['$ref']);
146+
$this->assertEquals('#/definitions/DummyDate', $json['definitions']['Issue6299.Issue6299OutputDto.jsonld']['allOf'][1]['properties']['collectionDto']['items']['$ref']);
147147
}
148148

149149
/**

0 commit comments

Comments
 (0)