Skip to content

OpenAPI 3.0.0 / Draft 4 spec: exclusiveMinimum/exclusiveMaximum emitted as numbers instead of booleans #7936

@Jules-Rabus

Description

@Jules-Rabus

API Platform version(s) affected: 4.2.24, also reproduces on main

Description

api:openapi:export --spec-version=3.0.0 emits exclusiveMinimum / exclusiveMaximum as numbers (JSON Schema 2020-12 form) for properties with Assert\Positive, Assert\GreaterThan, Assert\LessThan, or Assert\Range with strict bounds. OpenAPI 3.0 inherits JSON Schema Draft 4 semantics where these keywords MUST be booleans qualifying minimum / maximum.

BackwardCompatibleSchemaFactory (introduced in #6098 closing #6041) was supposed to handle this conversion but only fires when the serializer context contains draft_4 => true, a flag set only by ApiTestAssertionsTrait. OpenApiFactory::collectPaths() calls buildSchema(..., serializerContext: null, ...), so the BackwardCompatible factory never activates in the export / HTTP path.

How to reproduce

<?php
namespace App\Dto;

use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Metadata\ApiResource;   

#[ApiResource]  
final class CreateDepositReceipt
{
    #[Assert\Positive]
    public int $reminderFrequencyDays = 7;
}
php bin/console api:openapi:export --spec-version=3.0.0 --output=openapi_3.0.0.json

Observed:

"reminderFrequencyDays": {
  "minimum": 0,
  "exclusiveMinimum": 0,
  "default": 7,
  "type": "integer"
}

Expected (OpenAPI 3.0 / Draft 4):

"reminderFrequencyDays": {
  "minimum": 0,
  "exclusiveMinimum": true,
  "default": 7,
  "type": "integer"
}

Possible Solution

Wire spec_version === '3.0.0' from OpenApiFactory::__invoke($context) into the schema serializer context as [BackwardCompatibleSchemaFactory::SCHEMA_DRAFT4_VERSION => true], so the existing BC factory activates.

Additional Context

  • LegacyOpenApiNormalizer already keys on spec_version === '3.0.0' for late conversions (type[]anyOf, examplesexample) but does not touch exclusiveMinimum/exclusiveMaximum.
  • The HTTP entry point (OpenApiProvider::provideOpenApiFactory::__invoke($context)) already receives spec_version from the request via SerializerContextBuilder, so it benefits from the fix automatically.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions