Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ final class SchemaPropertyMetadataFactory implements PropertyMetadataFactoryInte
public function __construct(
ResourceClassResolverInterface $resourceClassResolver,
private readonly ?PropertyMetadataFactoryInterface $decorated = null,
private readonly bool $includeNullInNullableEnum = true,
) {
$this->resourceClassResolver = $resourceClassResolver;
}
Expand Down Expand Up @@ -184,7 +185,9 @@ private function applyNullability(array $schema, bool $isNullable): array
$schema['type'] = \is_array($currentType) ? array_merge($currentType, ['null']) : [$currentType, 'null'];

if (isset($schema['enum'])) {
$schema['enum'][] = null;
if ($this->includeNullInNullableEnum) {
$schema['enum'][] = null;
}

return $schema;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ public function testEnum(): void
$this->assertEquals(['type' => ['integer', 'null'], 'enum' => [1, 2, null]], $apiProperty->getSchema());
}

public function testEnumNullableIncludesNullByDefault(): void
{
$resourceClassResolver = $this->createMock(ResourceClassResolverInterface::class);
$apiProperty = new ApiProperty(nativeType: Type::nullable(Type::enum(IntEnumAsIdentifier::class)));
$decorated = $this->createMock(PropertyMetadataFactoryInterface::class);
$decorated->expects($this->once())->method('create')->with(DummyWithEnum::class, 'intEnumAsIdentifier')->willReturn($apiProperty);

// explicitly passing true (same as default) must still add null to enum
$schemaPropertyMetadataFactory = new SchemaPropertyMetadataFactory($resourceClassResolver, $decorated, includeNullInNullableEnum: true);
$apiProperty = $schemaPropertyMetadataFactory->create(DummyWithEnum::class, 'intEnumAsIdentifier');

$this->assertEquals(['type' => ['integer', 'null'], 'enum' => [1, 2, null]], $apiProperty->getSchema());
}

public function testEnumNullableExcludesNullWhenDisabled(): void
{
$resourceClassResolver = $this->createMock(ResourceClassResolverInterface::class);
$apiProperty = new ApiProperty(nativeType: Type::nullable(Type::enum(IntEnumAsIdentifier::class)));
$decorated = $this->createMock(PropertyMetadataFactoryInterface::class);
$decorated->expects($this->once())->method('create')->with(DummyWithEnum::class, 'intEnumAsIdentifier')->willReturn($apiProperty);

// with includeNullInNullableEnum=false, null must NOT appear in the enum array
$schemaPropertyMetadataFactory = new SchemaPropertyMetadataFactory($resourceClassResolver, $decorated, includeNullInNullableEnum: false);
$apiProperty = $schemaPropertyMetadataFactory->create(DummyWithEnum::class, 'intEnumAsIdentifier');

$schema = $apiProperty->getSchema();
$this->assertEquals(['integer', 'null'], $schema['type']);
$this->assertEquals([1, 2], $schema['enum']);
$this->assertNotContains(null, $schema['enum']);
}

public function testWithCustomOpenApiContext(): void
{
$resourceClassResolver = $this->createMock(ResourceClassResolverInterface::class);
Expand Down
3 changes: 2 additions & 1 deletion src/Laravel/ApiPlatformProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ public function register(): void
$app->make(ResourceClassResolverInterface::class)
),
$app->make(ResourceClassResolverInterface::class)
)
),
(bool) $config->get('api-platform.openapi.include_null_in_nullable_enum', true),
),
true === $config->get('app.debug') ? 'array' : $config->get('api-platform.cache', 'file')
);
Expand Down
1 change: 1 addition & 0 deletions src/Laravel/config/api-platform.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@

// 'openapi' => [
// 'tags' => [],
// 'include_null_in_nullable_enum' => true, // Whether null is included in the enum array for nullable enum properties in the JSON Schema.
// ],

'url_generation_strategy' => UrlGeneratorInterface::ABS_PATH,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ private function registerOpenApiConfiguration(ContainerBuilder $container, array
$container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
$container->setParameter('api_platform.openapi.license.identifier', $config['openapi']['license']['identifier']);
$container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);
$container->setParameter('api_platform.openapi.include_null_in_nullable_enum', $config['openapi']['include_null_in_nullable_enum']);

$tags = [];
foreach ($config['openapi']['tags'] as $tag) {
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Bundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
->info('To pass extra configuration to Scalar API Reference, like theme or darkMode.')
->end()
->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')->end()
->booleanNode('include_null_in_nullable_enum')->defaultTrue()->info('Whether null is included in the enum array for nullable enum properties in the JSON Schema.')->end()
->scalarNode('error_resource_class')->defaultNull()->info('The class used to represent errors in the OpenAPI documentation.')->end()
->scalarNode('validation_error_resource_class')->defaultNull()->info('The class used to represent validation errors in the OpenAPI documentation.')->end()
->end()
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Bundle/Resources/config/json_schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
->args([
service('api_platform.resource_class_resolver'),
service('api_platform.json_schema.metadata.property.metadata_factory.schema.inner'),
param('api_platform.openapi.include_null_in_nullable_enum'),
]);

$services->set('api_platform.json_schema.backward_compatible_schema_factory', BackwardCompatibleSchemaFactory::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
],
'swagger_ui_extra_configuration' => [],
'overrideResponses' => true,
'include_null_in_nullable_enum' => true,
'tags' => [],
'error_resource_class' => null,
'validation_error_resource_class' => null,
Expand Down
Loading