Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG][Python] Stringified Enum variable used in request path instead of underlying string #16688

Closed
5 of 6 tasks
john-odonnell opened this issue Sep 29, 2023 · 1 comment · Fixed by #16769
Closed
5 of 6 tasks

Comments

@john-odonnell
Copy link

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

When an Enum of strings is used as a path parameter, the request path contains a stringified constant variable name instead of the underlying string.

openapi-generator version

openapitools/openapi-generator-cli:latest

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: ""
  version: ""
paths:
  /items/{enumParam}:
    get:
      parameters:
      - name: enumParam
        in: path
        required: true
        schema:
          $ref: "#/components/schemas/EnumValue"
      responses:
        200:
            description: "Responds with the requested path"
            content:
              text/plain:
                schema:
                  type: string
components:
  schemas:
    EnumValue:
      type: string
      enum:
        - a
        - b
Generation Details

Generating using the Python client generator with default settings.

# ${PWD}/config.yml
generatorName: python
outputDir: /spec/python
inputSpec: /spec/openapi.yml
docker run --rm \
  -v "${PWD}":/spec \
  openapitools/openpai-generator-cli:latest \
    generate -c /spec/config.yml
Steps to reproduce
  1. Start a local web server that responds to requests with the requested resource path.

    # ${PWD}/echo_server.py
    import http.server
    import socketserver
    
    port = 80
    
    # Create a custom request handler that responds to all requests
    class CustomHandler(http.server.SimpleHTTPRequestHandler):
        def do_GET(self):
            # Extract the requested API path
            api_path = self.path
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(api_path.encode('utf-8'))
    
    # Create a socket server with the custom request handler
    with socketserver.TCPServer(('', port), CustomHandler) as httpd:
        print(f"Server running on port {port}")
        # Start the server
        httpd.serve_forever()
    $ python3 echo_server.py
    Server running on port 80
    
  2. Install the generated client:

    $ cd python
    $ python3 setup.py install --user
    

    This creates an EnumValue model:

    class EnumValue(str, Enum):
        """
        EnumValue
        """
    
        """
        allowed enum values
        """
        A = 'a'
        B = 'b'
    
        @classmethod
        def from_json(cls, json_str: str) -> EnumValue:
            """Create an instance of EnumValue from a JSON string"""
            return EnumValue(json.loads(json_str))
  3. Make requests to an endpoint that uses an Enum type as a path parameter:

    # #{PWD}/example.py
    import openapi_client
    from openapi_client.models import EnumValue
    
    configuration = openapi_client.Configuration(host = "http://localhost")
    
    with openapi_client.ApiClient(configuration) as api_client:
        api_instance = openapi_client.DefaultApi(api_client)
    
        print(api_instance.items_enum_param_get(EnumValue.A))
        print(api_instance.items_enum_param_get(EnumValue.A.value))
        print(api_instance.items_enum_param_get('a'))

    This code outputs the following:

    $ python3 example.py
    /items/EnumValue.A
    /items/EnumValue.A
    /items/EnumValue.A
    

    I expect it to output:

    $ python3 example.py
    /items/a
    /items/a
    /items/a
    
Related issues/PRs

I don't think there is an open issue that matches this one.

Suggest a fix

As a workaround in my own project, I updated api_client.mustache to check if path parameters are instances of aenum.Enum and dereference them:

for k, v in path_params:
+   # dereference Enums
+   if isinstance(v, Enum)
+       v = v.value
+
    # specified safe chars, encode everything
    resource_path = resource_path.replace(
        '{%s}' % k,
        quote(str(v), safe=config.safe_chars_for_path_param)
    )

Would probably need to do the same for query parameters, too.

@wing328
Copy link
Member

wing328 commented Oct 2, 2023

john-odonnell added a commit to cyberark/conjur-openapi-spec that referenced this issue Oct 2, 2023
john-odonnell added a commit to cyberark/conjur-openapi-spec that referenced this issue Oct 2, 2023
robertschweizer added a commit to robertschweizer/openapi-generator that referenced this issue Oct 9, 2023
wing328 added a commit that referenced this issue Oct 10, 2023
* test: Tests for enum params in path, query and header

* fix: Get enum ref values correctly in path parameters

Closes #16688

* fix java tests failure

---------

Co-authored-by: William Cheng <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants