nelmio / NelmioApiDocBundle

Generates documentation for your REST API from annotations
MIT License
2.23k stars 833 forks source link

[Bug]: Attribute-defined schemas containing oneOf/anyOf/allOf are merged with property types, no overwriting of types possible #2290

Open vuryss opened 4 months ago

vuryss commented 4 months ago

Version

4.26.1

Description

When trying to overwrite the type of a property with OA\Property attribute - using type or ref successfully replaced the types defined by the php itself. However when using oneOf, anyOf or allOf - those are instead merged with the PHP type definitions and it's impossible to overwrite them from attribute alone. The only way to overwrite them is to define them as schema in the config file and use a ref to point to the new schema. This is because of this line: https://github.com/nelmio/NelmioApiDocBundle/blob/9379c44da4b4e6ef36a81f3135d3d158838a7c16/src/ModelDescriber/ObjectModelDescriber.php#L174 which covers only type and ref. A very easy fix is to add the above 3 in the check.

Now why I need this? I have the following situation:

readonly class Rule
{
    public function __construct(
        public null|float|bool|int|string $value,
    ) {
    }
}

This generates the following spec:

                    "value": {
                        "nullable": true,
                        "oneOf": [
                            {
                                "type": "string",
                                "nullable": true
                            },
                            {
                                "type": "integer",
                                "nullable": true
                            },
                            {
                                "type": "number",
                                "format": "float",
                                "nullable": true
                            },
                            {
                                "type": "boolean",
                                "nullable": true
                            }
                        ]
                    }

However some validators like thephpleague/openapi-psr7-validator match integers to both type interger and type number which makes the validation of the schema of integer input invalid (it's one of, not any of and it matches 2 schemas).

To overcome this I try overwriting it like this:

        #[OA\Property(anyOf: [
            new OA\Schema(type: 'null'),
            new OA\Schema(type: 'number'),
            new OA\Schema(type: 'boolean'),
            new OA\Schema(type: 'integer'),
            new OA\Schema(type: 'string'),
        ])]
        public null|float|bool|int|string $value,

However this results into this spec:

                    "value": {
                        "nullable": true,
                        "anyOf": [
                            {
                                "type": "null"
                            },
                            {
                                "type": "number"
                            },
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "integer"
                            },
                            {
                                "type": "string"
                            }
                        ],
                        "oneOf": [
                            {
                                "type": "string",
                                "nullable": true
                            },
                            {
                                "type": "integer",
                                "nullable": true
                            },
                            {
                                "type": "number",
                                "format": "float",
                                "nullable": true
                            },
                            {
                                "type": "boolean",
                                "nullable": true
                            }
                        ]
                    }

Even if I use oneOf with less types like this:

        #[OA\Property(oneOf: [
            new OA\Schema(type: 'number'),
            new OA\Schema(type: 'boolean'),
            new OA\Schema(type: 'string', nullable: true),
        ])]
        public null|float|bool|int|string $value,

It merges them like that:

                    "value": {
                        "nullable": true,
                        "oneOf": [
                            {
                                "type": "number"
                            },
                            {
                                "type": "boolean"
                            },
                            {
                                "type": "string",
                                "nullable": true
                            },
                            {
                                "type": "string",
                                "nullable": true
                            },
                            {
                                "type": "integer",
                                "nullable": true
                            },
                            {
                                "type": "number",
                                "format": "float",
                                "nullable": true
                            },
                            {
                                "type": "boolean",
                                "nullable": true
                            }
                        ]
                    }
                }

So I cannot overwrite it with anyOf, oneOf or allOf. The only solution is to define in the config a schema like this:

            schemas:
                RuleValue:
                    anyOf:
                        -   type: string
                            nullable: true
                        -   type: number
                            nullable: true
                        -   type: boolean
                            nullable: true

And reference it like this:

        #[OA\Property(ref: '#/components/schemas/RuleValue')]
        public null|float|bool|int|string $value,

Additional context

No response