swagger-api / swagger-api/swagger-codegen

[GO] Error generating client, response body is consumed and returned as an empty pointer

Open
#10,485 4 comments 2 reactions 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

When generating go code from a swagger json like this:

   "/api/v2/projects/": {
      "get": {
        "description": "~edited",
        "operationId": "Projects_projects_list",
        "parameters": [
          {
            "description": "A page number within the paginated result set.",
            "in": "query",
            "name": "page",
            "required": false,
            "type": "integer"
          },
          {
            "description": "Number of results to return per page.",
            "in": "query",
            "name": "page_size",
            "required": false,
            "type": "integer"
          },
          {
            "description": "A search term.",
            "in": "query",
            "name": "search",
            "required": false,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": ""
          }
        },
        "summary": " List Projects",
        "tags": [
          "Projects"
        ]
      },
...

the following go code is generated:

func (a *ProjectsApiService) ProjectsProjectsList(ctx context.Context, localVarOptionals *ProjectsApiProjectsProjectsListOpts) (*http.Response, error) {
	var (
		localVarHttpMethod = strings.ToUpper("Get")
		localVarPostBody   interface{}
		localVarFileName   string
		localVarFileBytes  []byte
		
	)

	// create path and map variables
	localVarPath := a.client.cfg.BasePath + "/api/v2/projects/"

	localVarHeaderParams := make(map[string]string)
	localVarQueryParams := url.Values{}
	localVarFormParams := url.Values{}

	if localVarOptionals != nil && localVarOptionals.Page.IsSet() {
		localVarQueryParams.Add("page", parameterToString(localVarOptionals.Page.Value(), ""))
	}
	if localVarOptionals != nil && localVarOptionals.PageSize.IsSet() {
		localVarQueryParams.Add("page_size", parameterToString(localVarOptionals.PageSize.Value(), ""))
	}
	if localVarOptionals != nil && localVarOptionals.Search.IsSet() {
		localVarQueryParams.Add("search", parameterToString(localVarOptionals.Search.Value(), ""))
	}
	// to determine the Content-Type header
	localVarHttpContentTypes := []string{"application/json"}

	// set Content-Type header
	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
	if localVarHttpContentType != "" {
		localVarHeaderParams["Content-Type"] = localVarHttpContentType
	}

	// to determine the Accept header
	localVarHttpHeaderAccepts := []string{}

	// set Accept header
	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
	if localVarHttpHeaderAccept != "" {
		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
	}
	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
	if err != nil {
		return nil, err
	}

	localVarHttpResponse, err := a.client.callAPI(r)
	if err != nil || localVarHttpResponse == nil {
		return localVarHttpResponse, err
	}

	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)  //THE PROBLEM IS HERE
	localVarHttpResponse.Body.Close()
	if err != nil {
		return localVarHttpResponse, err
	}


	if localVarHttpResponse.StatusCode >= 300 {
		newErr := GenericSwaggerError{
			body: localVarBody,
			error: localVarHttpResponse.Status,
		}
		
		return localVarHttpResponse, newErr
	}

	return localVarHttpResponse, nil  //AND HERE
}

The problem in this function is that this line localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body) read the body of the response pointer and leave the localVarHttpResponse.Body empty. As a consequence the return localVarHttpResponse, nil returns the pointer to the response with the empty body.
This seems the case for any resource generated.

Swagger-codegen version

3.0.20

Swagger declaration file content or url

https://github.com/Roviluca/awx-go-autogenerated/blob/main/swagger.json

Command line used for generation

swagger-codegen generate -i ./swagger.json -l go -o ./test-codegen

Suggest a fix/enhancement

You can consume the content of the body only if needed by the return by moving the ReadAll inside the if checking for the response code >300.

if localVarHttpResponse.StatusCode >= 300 {
		localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
		localVarHttpResponse.Body.Close()
		if err != nil {
			return localVarHttpResponse, err
		}
		newErr := GenericSwaggerError{
			body: localVarBody,
			error: localVarHttpResponse.Status,
		}
		return localVarHttpResponse, newErr
	}

	return localVarHttpResponse, nil```

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 the Go client generation path invoked by swagger-codegen generate -i ./swagger.json -l go -o ./test-codegen, then trace the generated ProjectsProjectsList response handling and its callAPI entry point. Reproduce the behavior using the linked Swagger declaration and verify that successful responses retain a readable body while error responses still expose their body and status.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.