openapi-generators / openapi-generators/openapi-python-client

Incorrect generation of `_parse_response `

Open
#1,125 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
2k
Forks
293
Avg merge
34m
Merged PRs (30d)
1

Description

Lovely package BTW!

Describe the bug
Code generation for parsing API response does not generate the code that creates the Python model after the response is completed. This happens when using a $ref to define the response instead of just defining the response body inside the path definition.

OpenAPI Spec File

openapi: 3.0.3

info:
  title: Example
  description: |
    Example API definition.
  version: 1.0.0

security:
    - BearerAuth: []

paths:

  /v2/task/{task_id}:
    get:
      summary: Get the summary of a task.
      responses:
        '200':
          $ref: '#/components/responses/task_summary'
        '400':
          $ref: '#/components/responses/bad_request'
        '403':
          $ref: '#/components/responses/not_authorized'
      parameters:
        - $ref: '#/components/parameters/task_id'

components:

  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  parameters:
    task_id:
      name: task_id
      in: path
      description: The unique identifier of the task targeted by the request.
      required: true
      schema:
        $ref: '#/components/schemas/TaskId'

  responses:

    task_summary:
      description: Summary of the task at the time the request was made.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/TaskSummary'

    bad_submit_task_request:
      description: |
        The request is invalid. This may indicate an error when parsing a parameter, or an error when parsing or validating the request body. The response body may contain a JSON array with a list of validation errors.
      content:
        application/json:
          schema:
            type: object
            properties:
              validation_errors:
                type: array
                minItems: 1
                items:
                  type: string

    not_authorized:
      description: |
        User is not authorized to perform this task. 

    bad_request:
      description: |
        The request is invalid. This may indicate an error when parsing a parameter.

  schemas:
  
    TaskId:
      type: string
      minLength: 1
      maxLength: 256
      pattern: "^[a-zA-Z0-9][a-zA-Z0-9-]*$"
      example: '80cf75f2-4700-4006-9203-4376b091ee4e'
      description: A unique identifier for a task. Must not be an empty string.

    TaskSummary:
      type: "object"
      description: Summary of existing task, including task status.
      properties:
        task_status:
          $ref: '#/components/schemas/TaskStatus'
      required:
        - task_status

    TaskStatus:
      type: string
      enum:
        - Created
        - PayloadProcessing
        - Scheduled
        - Completed
        - Cancelled
        - Failed
      description: |
        Reports status of the task. Lifecycle of a normal task is Created -> PayloadProcessing -> Scheduled -> Completed.
        where PayloadProcessing - the task is going trough additional compilation/validation/optimization, 
        Scheduled - the task is in the qpu queue, Completed - the task has been executed.

the code that gets generated here is:

def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
    if response.status_code == HTTPStatus.OK:
        return None
    if response.status_code == HTTPStatus.BAD_REQUEST:
        return None
    if response.status_code == HTTPStatus.FORBIDDEN:
        return None
    if client.raise_on_unexpected_status:
        raise errors.UnexpectedStatus(response.status_code, response.content)
    else:
        return None

which is not correct. The additional context shows the expected behavior.

Desktop (please complete the following information):

  • OS: macOS: 14.4.1
  • Python Version: 3.12.1
  • openapi-python-client: 0.21.5 [e.g. 0.1.0]

Additional context
Note if I modify the path:

  /v2/task/{task_id}:
    get:
      summary: Get the summary of a task.
      responses:
        '200':
          description: Summary of the task at the time the request was made.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TaskSummary'
        '400':
          $ref: '#/components/responses/bad_request'
        '403':
          $ref: '#/components/responses/not_authorized'
      parameters:
        - $ref: '#/components/parameters/task_id'

it generates the correct code:

def _parse_response(
    *, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[Any, TaskSummary]]:
    if response.status_code == HTTPStatus.OK:
        response_200 = TaskSummary.from_dict(response.json())

        return response_200
    if response.status_code == HTTPStatus.BAD_REQUEST:
        response_400 = cast(Any, None)
        return response_400
    if response.status_code == HTTPStatus.FORBIDDEN:
        response_403 = cast(Any, None)
        return response_403
    if client.raise_on_unexpected_status:
        raise errors.UnexpectedStatus(response.status_code, response.content)
    else:
        return None

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

Reproduce the generated client using the supplied OpenAPI specification, comparing the response parsing path when the response is defined inline versus through a $ref. Trace the code-generation path that builds _parse_response; done means the referenced 200 response generates TaskSummary.from_dict(response.json()) while other statuses retain their expected handling.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.