swagger-api / swagger-api/swagger-codegen

[python-flask] model instantiation through dict does not respect required default properties

Open
#11,940 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Mustache
Stars
17.8k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

Description

each generated "model" has the method from_dict which is used in the controller in order to deserialize the JSON into an actual instance of the dto. when calling the endpoint this request will fail, because the default parameters are not respected in the call-chain.

curl -d 'some_json_does_not_matter' http://localhost:3000/your/endpoint
ERROR in app: Exception on /api/rest/v1/train [POST]
Traceback (most recent call last):
  # bla bla, I've stripped some here

  File "/y/controllers/my_controller.py", line 23, in initialize_model_version
    model_metadata = MyDto.from_dict(connexion.request.get_json())  # noqa: E501
  File "/y/models/mydto_metadata.py", line 45, in from_dict
    return util.deserialize_model(dikt, cls)
  File "/y/util.py", line 108, in deserialize_model
    instance = klass()
  File "/y/models/mydto_metadata.py", line 32, in __init__
    self.sentences = sentences
  File "/y/models/mydto_metadata.py", line 66, in sentences
    raise ValueError("Invalid value for `anything`, must not be `None`")  # noqa: E501
ValueError: Invalid value for `anything`, must not be `None`
127.0.0.1 - - "POST /your/endpoint HTTP/1.1" 500 -

I've briefly scrolled through the open issues, but found non containing this error. some more details with links containing the template references below.

controller, we receive the request here:

def foo(my_dto=None):  # noqa: E501
    if connexion.request.is_json:
        my_dto= MyDto.from_dict(connexion.request.get_json())  # noqa: E501

dto, the constructor with parameters is problematic:

class MyDto(Model):
    def __init__(self, sentences=None):  # noqa: E501
        # some init stuff here

        self.anything= anything # this line fails, because the setter checks the required property

    @classmethod
    def from_dict(cls, dikt) -> 'MyDto':
        return util.deserialize_model(dikt, cls)

    @anything.setter
    def anything(self, anything):
        if anything is None:
            raise ValueError("Invalid value for `anything`, must not be `None`")  # noqa: E501

and the utility class (util.py):

def deserialize_model(data, klass):
    instance = klass() # <-- this is where the server throws an error

    if not instance.openapi_types:
        return data

    for attr, attr_type in six.iteritems(instance.openapi_types):
        # sets the attributes here, not in the constructor
Swagger-codegen version

through the gradle plugin org.openapi.generator, version 6.1.0:

openApiGenerate {
    generatorName = "python-flask"
    inputSpec = "${project.rootDir}/specs/api.yml".toString()
    outputDir = "${project.buildDir}/generated-resources".toString()
}
Swagger declaration file content or url

doesn't really matter, but it's important that the property is required!

components:
  schemas:
    MyDto:
      type: object
      required:
        - anything
      properties:
        anything:
          type: string
Command line used for generation

as said, through the gradle plugin.

Steps to reproduce

I think you could generate a server using the petstore API, but haven't tried it.

Related issues/PRs

didn't find any.

Suggest a fix/enhancement

now the problem is you cannot just alter the constructor, because when initializing the class using the constructor you probably want that check to happen. you just don't want it to happen when calling from_dict. I think, the only way around this is using an empty constructor and a "builder-method" (another @classmethod) as replacement for an "all-args-constructor". but, this change would be breaking.

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 with modules/swagger-codegen/src/main/resources/flaskConnexion/model.mustache and util.mustache, then inspect controller.mustache to follow the generated from_dict path. Reproduce the required-property case with a python-flask server generated from the supplied schema; done means deserialization no longer fails during empty model initialization while direct constructor validation remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, python
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.