OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.32k stars 6.45k forks source link

[BUG][python] string format password throws AttributeError: 'SecretStr' object has no attribute 'to_dict' #16086

Open MAKOMO opened 1 year ago

MAKOMO commented 1 year ago

Bug Report Checklist

Description

Calling a method with a credential object holding a password attribute of type string with format: password fails in the generated Python client code.

openapi-generator version

I tested this on the current v7.0.0-beta as well as with todays trunk using the python (was python-nextgen) generator.

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: ""
  version: ""
paths:
  /accounts/users/authenticate:
    post:
      operationId: authenticate
      summary: Authenticate user
      description: Authenticates user and returns bearer token
      tags:
        - account
      security: []
      requestBody:
        description: User credentials
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Credentials"
      responses:
        200:
            description: OK
components:
  schemas:
    Credentials:
      type: object
      properties:
        email:
          type: string
          description: email address
          example: me@work.com
        password:
          type: string
          format: password
          description: password
          example: secret
      required:
        - email
        - password
Generation Details
java -jar openapi-generator-cli/target/openapi-generator-cli.jar generate -i openapi-schema.yaml -g python
Steps to reproduce
import time
import os
import openapi_client
from openapi_client.rest import ApiException
from pprint import pprint

configuration = openapi_client.Configuration(
    host = "http://localhost"
)

with openapi_client.ApiClient(configuration) as api_client:
    api_instance = openapi_client.AccountApi(api_client)
    credentials = openapi_client.Credentials(email="me@email.com", password="123")
    api_response = api_instance.authenticate(credentials)

fails with

Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
  File "pydantic/decorator.py", line 40, in pydantic.decorator.validate_arguments.validate.wrapper_function
  File "pydantic/decorator.py", line 134, in pydantic.decorator.ValidatedFunction.call
  File "pydantic/decorator.py", line 206, in pydantic.decorator.ValidatedFunction.execute
  File "./openapi_client/api/account_api.py", line 73, in authenticate
    return self.authenticate_with_http_info(credentials, **kwargs)  # noqa: E501
[ cut ]
  File "./openapi_client/api_client.py", line 291, in sanitize_for_serialization
    obj_dict = obj.to_dict()
               ^^^^^^^^^^^
AttributeError: 'SecretStr' object has no attribute 'to_dict'
wing328 commented 1 year ago

thanks for reporting the issue, will try to fix it this weekend.

azdolinski commented 9 months ago

/modules/openapi-generator/src/main/resources/python/api_client.mustache not all objects include to_dict()

replace:

        elif isinstance(obj, dict):
            obj_dict = obj
        else:
            # Convert model obj to dict except
            # attributes `openapi_types`, `attribute_map`
            # and attributes which value is not None.
            # Convert attribute name to json key in
            # model definition for request.
            obj_dict = obj.to_dict()                   <------ !!!

        return {
            key: self.sanitize_for_serialization(val)
            for key, val in obj_dict.items()
        }

by:

obj_dict = obj.__dict__

__dict__ could not always work.. if object will be nested - so for nested objects:

obj_dict = json.loads(json.dumps(obj, default=lambda o: o.__dict__))
zishunwei commented 6 months ago

Hi @wing328 @azdolinski , I still kicked the same error here about SecretStr, looks you guys have some discussion and fix, but the issue is still existed while I used the latest master code to generate python binding.

Looks this fix mentioned in https://github.com/OpenAPITools/openapi-generator/pull/17310 not merged

elif isinstance(obj, SecretStr):
            return obj.get_secret_value()

What is the current status of this issue? Thanks

wing328 commented 6 months ago

can you please file a new PR with the fix instead when you've time?

zishunwei commented 6 months ago

I noticed @azdolinski has a PR ready for this: https://github.com/azdolinski/openapi-generator/pull/2

mathmul commented 5 months ago

Just sharing my experience if it is by some chance of any help to anyone.

# Working:
> docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i /local/rest-api-server/src/main/resources/openapi.yaml -g python -o /local/web-client/openapi_client
# Not working:
> docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli:v7.4.0 generate -i /local/rest-api-server/src/main/resources/openapi.yaml -g python -o /local/web-client/openapi_client
                                                                     ↑↑↑↑↑↑↑

I mean using just that works, but then when I use the generated client for creating a user, I get the error from the title of this issue.

openapitools/openapi-generator-cli is equivalent to openapitools/openapi-generator-cli:latest which I was sure is equal to openapitools/openapi-generator-cli:v7.4.0 at the time of writing. Then I tested and saw the latest points to a snapshot!

> docker run --rm openapitools/openapi-generator-cli:latest version                                                                                                                   
7.5.0-SNAPSHOT  

Oddly enough this isn't allowed:

> docker run --rm -v ${PWD}:/local openapitools/openapi-generator-cli:v7.5.0-SNAPSHOT generate -i /local/rest-api-server/src/main/resources/openapi.yaml -g python -o /local/web-client/openapi_client
docker: Error response from daemon: manifest for openapitools/openapi-generator-cli:v7.5.0-SNAPSHOT not found: manifest unknown: manifest unknown.

Does this mean the solution for me is to simply wait until a certain unknow date in the future, and things will work once the openapitools/openapi-generator-cli:v7.5.0 is available?

Seems like I started with my first python/flask project at almost the right time :)