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][Go] client returns pointer address instead of the error message when the response has optional title/description field #20553

Open
5 of 6 tasks
itotake-coconala opened this issue Jan 29, 2025 · 0 comments

Comments

@itotake-coconala
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)

When an error response type has optional title and description fields, the generated Go client returns a pointer address instead of the actual message content.

Description

The generated Go client incorrectly formats error messages when the responded problem object contains optional title and detail fields.
Instead of displaying the string values of these fields, the client prints their memory addresses.

sample client code

package main

import (
	"context"
	"fmt"

	"github.com/itotake-coconala/openapi-issue-pointeraddr/openapi"
)

func main() {
	client := openapi.NewAPIClient(openapi.NewConfiguration())

	_, err := client.DefaultAPI.Call(context.Background()).Execute()
	if err != nil {
		fmt.Println(err)
	}
}

output

$ go run ./client/
400 Bad Request %!s(*string=0x40001de0d0) (%!s(*string=0x40001de0e0))

sample server code

package main

import (
	"encoding/json"
	"net/http"
)

func main() {
	http.HandleFunc("/call", callHandler)

	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

type Problem struct {
	Type     string `json:"type"`
	Title    string `json:"title"`
	Detail   string `json:"detail"`
	Instance string `json:"instance"`
}

func callHandler(w http.ResponseWriter, r *http.Request) {
	problem := Problem{
		Type:     "",
		Title:    "invalid request",
		Detail:   "This endpoint always returnes error",
		Instance: "/call",
	}

	w.Header().Set("Content-Type", "application/problem+json")
	w.WriteHeader(400)

	if err := json.NewEncoder(w).Encode(problem); err != nil {
		panic(err)
	}
}
openapi-generator version

v7.11.0

OpenAPI declaration file content or url
openapi: 3.1.0
info:
  title: error response with optional title & detail
  version: 0.0.1
servers:
- url: http://localhost:8080
paths:
  /call:
    post:
      operationId: call
      requestBody:
        content:
          application/json:
            schema:
              properties: {}
              type: object
      responses:
        "200":
          description: OK
        "400":
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
          description: error response
      summary: the api causing error
components:
  schemas:
    Problem:
      properties:
        type:
          type: string
        title:
          type: string
        detail:
          type: string
        instance:
          type: string
Generation Details
docker run --rm \
	-u $(shell id -u):$(shell id -g) \
	-v ${PWD}:/local openapitools/openapi-generator-cli:v7.11.0 generate \
	-i /local/openapi.yaml \
	-g go \
	-o /local/openapi \
	--git-user-id itotake-coconala --git-repo-id openapi-issue-pointeraddr/openapi \
	--additional-properties=withGoMod=false
Steps to reproduce
  1. generate OpenAPI client
  2. run server like go run ./server/
  3. run client like go run ./client/
Related issues/PRs
Suggest a fix

In the generated API client, formatErrorMessage function formats error message using title/detail field in the response.
If these fields are optional, their types are *string and formatErrorMessage prints the address of them instead of the value.

Current formatErrorMessage code:

// format error message using title and detail when model implements rfc7807
func formatErrorMessage(status string, v interface{}) string {
	str := ""
	metaValue := reflect.ValueOf(v).Elem()

	if metaValue.Kind() == reflect.Struct {
		field := metaValue.FieldByName("Title")
		if field != (reflect.Value{}) {
			str = fmt.Sprintf("%s", field.Interface())
		}

		field = metaValue.FieldByName("Detail")
		if field != (reflect.Value{}) {
			str = fmt.Sprintf("%s (%s)", str, field.Interface())
		}
	}

	return strings.TrimSpace(fmt.Sprintf("%s %s", status, str))
}

It should be like below to print the value of pointers.

// format error message using title and detail when model implements rfc7807
func formatErrorMessage(status string, v interface{}) string {
	str := ""
	metaValue := reflect.ValueOf(v).Elem()

	if metaValue.Kind() == reflect.Struct {
		field := metaValue.FieldByName("Title")
		if field.Kind() == reflect.Pointer {
			field = field.Elem()
		}
		if field != (reflect.Value{}) {
			str = fmt.Sprintf("%s", field.Interface())
		}

		field = metaValue.FieldByName("Detail")
		if field.Kind() == reflect.Pointer {
			field = field.Elem()
		}
		if field != (reflect.Value{}) {
			str = fmt.Sprintf("%s (%s)", str, field.Interface())
		}
	}

	return strings.TrimSpace(fmt.Sprintf("%s %s", status, str))
}

The output with modified client:

$ go run ./client
400 Bad Request invalid request (This endpoint always returnes error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant