Skip to content

Commit cc8dc66

Browse files
committedFeb 20, 2020
Use {"nullable": true, "anyOf": [{"$ref": ...}]} to comply with OpenAPI 3.0
OpenAPI 3.1 is not yet released, but fixes nullability in the way we had fixed it before (via `{"oneOf": [{"type": "null"}, ...]}`) in OAI/OpenAPI-Specification#1977. Until OpenAPI 3.1 is released, things like ``{"type": ["integer", "null"]}` are not valid definitions (because `"null"` is not yet a recognized type). We'll stick to OpenAPI 3.0 for now, using: * `{"nullable": true, ...}` for simple types * `{"nullable": true, "anyOf": [{"$ref": ...}]}` for type references
1 parent 03edb55 commit cc8dc66

File tree

3 files changed

+36
-49
lines changed

3 files changed

+36
-49
lines changed
 

‎src/JsonSchema/TypeFactory.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,13 @@ private function addNullabilityToTypeDefinition(array $jsonSchema, Type $type, a
180180
return $jsonSchema;
181181
}
182182

183-
return [
184-
'oneOf' => [
185-
['type' => 'null'],
186-
$jsonSchema,
187-
],
188-
];
183+
if (\array_key_exists('$ref', $jsonSchema)) {
184+
return [
185+
'nullable' => true,
186+
'anyOf' => [$jsonSchema],
187+
];
188+
}
189+
190+
return array_merge($jsonSchema, ['nullable' => true]);
189191
}
190192
}

‎tests/JsonSchema/TypeFactoryTest.php

+23-34
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,38 @@ class TypeFactoryTest extends TestCase
2929
public function testGetType(array $schema, Type $type): void
3030
{
3131
$typeFactory = new TypeFactory();
32-
$this->assertSame($schema, $typeFactory->getType($type));
32+
$this->assertEquals($schema, $typeFactory->getType($type));
3333
}
3434

3535
public function typeProvider(): iterable
3636
{
3737
yield [['type' => 'integer'], new Type(Type::BUILTIN_TYPE_INT)];
38-
yield [['oneOf' => [['type' => 'null'], ['type' => 'integer']]], new Type(Type::BUILTIN_TYPE_INT, true)];
38+
yield [['nullable' => true, 'type' => 'integer'], new Type(Type::BUILTIN_TYPE_INT, true)];
3939
yield [['type' => 'number'], new Type(Type::BUILTIN_TYPE_FLOAT)];
40-
yield [['oneOf' => [['type' => 'null'], ['type' => 'number']]], new Type(Type::BUILTIN_TYPE_FLOAT, true)];
40+
yield [['nullable' => true, 'type' => 'number'], new Type(Type::BUILTIN_TYPE_FLOAT, true)];
4141
yield [['type' => 'boolean'], new Type(Type::BUILTIN_TYPE_BOOL)];
42-
yield [['oneOf' => [['type' => 'null'], ['type' => 'boolean']]], new Type(Type::BUILTIN_TYPE_BOOL, true)];
42+
yield [['nullable' => true, 'type' => 'boolean'], new Type(Type::BUILTIN_TYPE_BOOL, true)];
4343
yield [['type' => 'string'], new Type(Type::BUILTIN_TYPE_STRING)];
44-
yield [['oneOf' => [['type' => 'null'], ['type' => 'string']]], new Type(Type::BUILTIN_TYPE_STRING, true)];
44+
yield [['nullable' => true, 'type' => 'string'], new Type(Type::BUILTIN_TYPE_STRING, true)];
4545
yield [['type' => 'object'], new Type(Type::BUILTIN_TYPE_OBJECT)];
46-
yield [['oneOf' => [['type' => 'null'], ['type' => 'object']]], new Type(Type::BUILTIN_TYPE_OBJECT, true)];
46+
yield [['nullable' => true, 'type' => 'object'], new Type(Type::BUILTIN_TYPE_OBJECT, true)];
4747
yield [['type' => 'string', 'format' => 'date-time'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTimeImmutable::class)];
48-
yield [['oneOf' => [['type' => 'null'], ['type' => 'string', 'format' => 'date-time']]], new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTimeImmutable::class)];
48+
yield [['nullable' => true, 'type' => 'string', 'format' => 'date-time'], new Type(Type::BUILTIN_TYPE_OBJECT, true, \DateTimeImmutable::class)];
4949
yield [['type' => 'string', 'format' => 'duration'], new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateInterval::class)];
5050
yield [['type' => 'object'], new Type(Type::BUILTIN_TYPE_OBJECT, false, Dummy::class)];
51-
yield [['oneOf' => [['type' => 'null'], ['type' => 'object']]], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
51+
yield [['nullable' => true, 'type' => 'object'], new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class)];
5252
yield [['type' => 'array', 'items' => ['type' => 'string']], new Type(Type::BUILTIN_TYPE_STRING, false, null, true)];
5353
yield 'array can be itself nullable' => [
54-
['oneOf' => [['type' => 'null'], ['type' => 'array', 'items' => ['type' => 'string']]]],
54+
['nullable' => true, 'type' => 'array', 'items' => ['type' => 'string']],
5555
new Type(Type::BUILTIN_TYPE_STRING, true, null, true),
5656
];
5757

5858
yield 'array can contain nullable values' => [
5959
[
6060
'type' => 'array',
6161
'items' => [
62-
'oneOf' => [
63-
['type' => 'null'],
64-
['type' => 'string'],
65-
],
62+
'nullable' => true,
63+
'type' => 'string',
6664
],
6765
],
6866
new Type(Type::BUILTIN_TYPE_STRING, false, null, true, null, new Type(Type::BUILTIN_TYPE_STRING, true, null, false)),
@@ -81,10 +79,9 @@ public function typeProvider(): iterable
8179

8280
yield 'nullable map with string keys becomes a nullable object' => [
8381
[
84-
'oneOf' => [
85-
['type' => 'null'],
86-
['type' => 'object', 'additionalProperties' => ['type' => 'string']],
87-
],
82+
'nullable' => true,
83+
'type' => 'object',
84+
'additionalProperties' => ['type' => 'string'],
8885
],
8986
new Type(
9087
Type::BUILTIN_TYPE_STRING,
@@ -112,10 +109,8 @@ public function typeProvider(): iterable
112109
[
113110
'type' => 'object',
114111
'additionalProperties' => [
115-
'oneOf' => [
116-
['type' => 'null'],
117-
['type' => 'integer'],
118-
],
112+
'nullable' => true,
113+
'type' => 'integer',
119114
],
120115
],
121116
new Type(
@@ -130,17 +125,11 @@ public function typeProvider(): iterable
130125

131126
yield 'nullable map can contain nullable values' => [
132127
[
133-
'oneOf' => [
134-
['type' => 'null'],
135-
[
136-
'type' => 'object',
137-
'additionalProperties' => [
138-
'oneOf' => [
139-
['type' => 'null'],
140-
['type' => 'integer'],
141-
],
142-
],
143-
],
128+
'nullable' => true,
129+
'type' => 'object',
130+
'additionalProperties' => [
131+
'nullable' => true,
132+
'type' => 'integer',
144133
],
145134
],
146135
new Type(
@@ -297,8 +286,8 @@ public function testGetClassTypeWithNullability(): void
297286

298287
self::assertSame(
299288
[
300-
'oneOf' => [
301-
['type' => 'null'],
289+
'nullable' => true,
290+
'anyOf' => [
302291
['$ref' => 'the-ref-name'],
303292
],
304293
],

‎tests/Swagger/Serializer/DocumentationNormalizerV3Test.php

+5-9
Original file line numberDiff line numberDiff line change
@@ -385,13 +385,9 @@ private function doTestNormalize(OperationMethodResolverInterface $operationMeth
385385
'description' => 'This is an initializable but not writable property.',
386386
]),
387387
'dummyDate' => new \ArrayObject([
388-
'oneOf' => [
389-
['type' => 'null'],
390-
[
391-
'type' => 'string',
392-
'format' => 'date-time',
393-
],
394-
],
388+
'nullable' => true,
389+
'type' => 'string',
390+
'format' => 'date-time',
395391
'description' => 'This is a \DateTimeInterface object.',
396392
]),
397393
],
@@ -2005,8 +2001,8 @@ public function testNormalizeWithNestedNormalizationGroups(): void
20052001
]),
20062002
'relatedDummy' => new \ArrayObject([
20072003
'description' => 'This is a related dummy \o/.',
2008-
'oneOf' => [
2009-
['type' => 'null'],
2004+
'nullable' => true,
2005+
'anyOf' => [
20102006
['$ref' => '#/components/schemas/'.$relatedDummyRef],
20112007
],
20122008
]),

0 commit comments

Comments
 (0)