OpenAPITools / OpenAPITools/openapi-generator

[BUG][PYTHON] Validation on dict values with enum not working correctly

Open
#19,274 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The model validation is wrong for dictionaries with values in Enums. The fields_validate_enum method is not expecting a dict as input.

openapi-generator version

Docker image: openapitools/openapi-generator-cli
Versions: v7.7.0

OpenAPI declaration file content or url

YAML code

Generation Details

Generated via Docker, using the following command:

docker run --rm -v $(pwd):/api openapitools/openapi-generator-cli:v7.7.0 \
  generate -g python \
  -c /api/python.yml \
  -i /api/openapi.yaml \
  -o /api/clients/python

Here is the python.yml config file:

packageName: python_client
projectName: python-client
packageVersion: 2.2.0
hideGenerationTimestamp: true
generateSourceCodeOnly: false
mapNumberTo: Union[StrictFloat, StrictInt]
datetimeFormat: "%Y-%m-%dT%H:%M:%S%z"
dateFormat: "%Y-%m-%d"
useOneOfDiscriminatorLookup: false
library: urllib3
disallowAdditionalPropertiesIfNotPresent: true
Steps to reproduce

OS: Ubuntu 22.04
Python: 3.10.12

  1. curl https://gist.githubusercontent.com/vcutrona/adb7571338fce6cb5c495ba4abd725f7/raw/b736a812adcaac6a118f0f7c8d714f81084b63c4/openapi.yaml -o openapi.yaml
  2. Generate the python.yml file with the config specified above
  3. docker run --rm -v $(pwd):/api openapitools/openapi-generator-cli:v7.7.0 generate -g python -c /api/python.yml -i /api/openapi.yaml -o /api/clients/python
  4. Install the new client with pip install -e clients/python
  5. execute the following code snippet:
from python_client.models import MeasurementDescriptorDto
MeasurementDescriptorDto(fields={"aField": "STRING", "anotherField": "BOOLEAN"})

Exception thrown:

    if value not in set(['STRING', 'BOOLEAN', 'NUMBER']):
TypeError: unhashable type: 'dict'
Related issues/PRs
Suggest a fix
  • #19316

In my example (OAS 3.0), fields is a key-value dictionary (defined as documented here).
Also, the additionalProperties keyword is used to specify the type of values in that dictionary.
In my example, fields accepts strings as values, in particular only those matching an enum (i.e., "STRING", "BOOLEAN", "NUMBER").

The python generator can indeed recognize this spec, generating the MeasurementDescriptorDto class as it follows:

class MeasurementDescriptorDto(BaseModel):
    """
    MeasurementDescriptorDto
    """ # noqa: E501
    id: Optional[StrictStr] = None
    name: Optional[StrictStr] = None
    description: Optional[StrictStr] = None
    unit_of_measure_id: Optional[StrictStr] = Field(default=None, alias="unitOfMeasureId")
    taxonomy_item_id: Optional[StrictStr] = Field(default=None, alias="taxonomyItemId")
    scale_id: Optional[StrictStr] = Field(default=None, alias="scaleId")
    factory_entity_model_id: Optional[StrictStr] = Field(default=None, alias="factoryEntityModelId")
    categories_id: Optional[List[StrictStr]] = Field(default=None, alias="categoriesId")
    functional_module_inputs_id: Optional[List[StrictStr]] = Field(default=None, alias="functionalModuleInputsId")
    fields: Optional[Dict[str, StrictStr]] = None
    output_maps_id: Optional[List[StrictStr]] = Field(default=None, alias="outputMapsId")
    __properties: ClassVar[List[str]] = ["id", "name", "description", "unitOfMeasureId", "taxonomyItemId", "scaleId", "factoryEntityModelId", "categoriesId", "functionalModuleInputsId", "fields", "outputMapsId"]

Please note the type of fields declared as Optional[Dict[str, StrictStr]].
Given that fields is a dictionary (optional), the following validator cannot work.
Indeed, the fields_validate_enum is not expecting a dictionary as input:

    @field_validator('fields')
    def fields_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(['STRING', 'BOOLEAN', 'NUMBER']):
            raise ValueError("must be one of enum values ('STRING', 'BOOLEAN', 'NUMBER')")
        return value

The suggested PR #19316 applies the validation function to the values of the dictionary (i.e., value.values()), not to the dictionary itself (i.e., value).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start from the generated MeasurementDescriptorDto example and trace the fields_validate_enum validator in the Python generator output. Reproduce the failure with the supplied OpenAPI file and Docker command; done means dictionary values such as STRING, BOOLEAN, and NUMBER are validated without treating the dictionary itself as an enum value.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.