Repository: go-swagger/go-swagger (go)

Issue:
Generated client code prints a pointer in Error() func
## Problem statement

Generated code tries to print a pointer instead of printing the value pointed to.

Client code generated via:
```
docker run --rm -it -u $(shell id -u):$(shell id -u) -e GOPATH=$(GOPATH):/go -v $(HOME):$(HOME) -w $(shell pwd) quay.io/goswagger/swagger:v0.27.0 generate client --name abc -f ./api/swagger.yaml
```
Example of generated client code in `*_responses.go`:
```
type CreateConflict struct {
	Payload *models.Error
}

func (o *CreateConflict) Error() string {
	return fmt.Sprintf("[POST /abc][%d] createConflict  %+v", 409, o.Payload)
}
```

## Swagger specification

Relevant sections:
```
swagger: "2.0"
...
paths:
  /abc:
    post:
      tags:
        - "abc"
      summary: "Create a new ABC"
      description: ""
      operationId: "create"
      consumes:
        - "application/json"
      produces:
        - "application/json"
      parameters:
        - in: "body"
          name: "abc"
          required: true
          schema:
            $ref: "#/definitions/abc"
      responses:
        202:
          description: "Accepted"
          schema:
            $ref: '#/definitions/abc'
        400:
          $ref: '#/responses/BadRequest'
        404:
          $ref: '#/responses/NotFound'
        409:
          $ref: '#/responses/Conflict'
        500:
          $ref: '#/responses/InternalError'
...
definitions:
  Error:
    type: "object"
    properties:
      message:
        type: string
        x-nullable: false
        minLength: 1
    required:
      - message
responses:
  Unauthorized:
    description: "Unauthorized"
    schema:
      $ref: '#/definitions/Error'
  NotFound:
    description: "Not found"
    schema:
      $ref: '#/definitions/Error'
  InternalError:
    description: "Inernal Server Error"
    schema:
      $ref: '#/definitions/Error'
  BadRequest:
    description: "Bad Request"
    schema:
      $ref: '#/definitions/Error'
  Conflict:
    description: "Conflict"
    schema:
      $ref: '#/definitions/Error'
```

## Environment
swagger version: quay.io/goswagger/swagger:v0.27.0 and 0.24.0 tested
go version: 1.15
OS: MacOS 11.3


Pull request description:
fix(client): alleviates issues with pointers in error reporting
As suggested in #2590, this PR replaces
fmt.Sprintf("%+v", somePayloadPossiblyAPointer)
by a JSON representation of the payload

This is only enabled when the response has a Schema and this is not a stream.

* fixes #2590

Patch:
diff --git a/fixtures/bugs/2590/2590.yaml b/fixtures/bugs/2590/2590.yaml
new file mode 100644
index 00000000..7af3bd69
--- /dev/null
+++ b/fixtures/bugs/2590/2590.yaml
@@ -0,0 +1,76 @@
+swagger: "2.0"
+info:
+  version: "1.0.0"
+  title: generating client with error reporting
+  description: verify that generated code prints errors
+paths:
+  /abc:
+    post:
+      tags:
+        - "abc"
+      summary: "Create a new ABC"
+      description: ""
+      operationId: "create"
+      consumes:
+        - "application/json"
+      produces:
+        - "application/json"
+      parameters:
+        - in: "body"
+          name: "abc"
+          required: true
+          schema:
+            $ref: "#/definitions/abc"
+      responses:
+        202:
+          description: "Accepted"
+          schema:
+            $ref: '#/definitions/abc'
+        400:
+          $ref: '#/responses/BadRequest'
+        404:
+          $ref: '#/responses/NotFound'
+        409:
+          $ref: '#/responses/Conflict'
+        500:
+          $ref: '#/responses/InternalError'
+        default:
+          description: "default response"
+          schema:
+
+definitions:
+  abc:
+    type: string
+
+  Error:
+    type: "object"
+    properties:
+      message:
+        type: string
+        x-nullable: false
+        minLength: 1
+    required:
+      - message
+
+responses:
+  Unauthorized:
+    description: "Unauthorized"
+    schema:
+      $ref: '#/definitions/Error'
+  NotFound:
+    description: "Not found"
+    schema:
+      $ref: '#/definitions/Error'
+  InternalError:
+    description: "Inernal Server Error"
+    schema:
+      $ref: '#/definitions/Error'
+  BadRequest:
+    description: "Bad Request"
+    schema:
+      $ref: '#/definitions/Error'
+  Conflict:
+    description: "Conflict"
+    schema:
+      $ref: '#/definitions/Error'
+
diff --git a/generator/templates/client/response.gotmpl b/generator/templates/client/response.gotmpl
index d6223854..7c6b92cf 100644
--- a/generator/templates/client/response.gotmpl
+++ b/generator/templates/client/response.gotmpl
@@ -119,11 +119,17 @@ func ({{ .ReceiverName }} *{{ pascalize .Name }}) Code() int {
 }
 
 func ({{ .ReceiverName }} *{{ pascalize .Name }}) Error() string {
-	return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown error {{ end }}{{ if .Schema }} %+v{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}, o.Payload{{ end }})
+  {{- if .Schema }}{{ if (not .Schema.IsStream) }}
+  payload, _ := json.Marshal(o.Payload)
+  {{- end }}{{- end }}
+	return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown error {{ end }}{{ if .Schema }} %s{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}{{ if not .Schema.IsStream }}, payload{{ end }}{{ end }})
 }
 
 func ({{ .ReceiverName }} *{{ pascalize .Name }}) String() string {
-	return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown response {{ end }}{{ if .Schema }} %+v{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}, o.Payload{{ end }})
+  {{- if .Schema }}{{ if (not .Schema.IsStream) }}
+  payload, _ := json.Marshal(o.Payload)
+  {{- end }}{{- end }}
+	return fmt.Sprintf("[{{ upper .Method }} {{ .Path }}][%d] {{ if .Name }}{{ .Name }} {{ else }}unknown response {{ end }}{{ if .Schema }} %s{{ end }}", {{ if eq .Code -1 }}{{ .ReceiverName }}._statusCode{{ else }}{{ .Code }}{{ end }}{{ if .Schema }}{{ if not .Schema.IsStream }}, payload{{ end }}{{ end }})
 }
 
 {{ if .Schema }}


Test patch:
diff --git a/generator/client_test.go b/generator/client_test.go
index a14c9d0a..872787e7 100644
--- a/generator/client_test.go
+++ b/generator/client_test.go
@@ -1105,3 +1105,45 @@ func TestGenClient_909_6(t *testing.T) {
 		}
 	}
 }
+
+func TestGenClient_2590(t *testing.T) {
+	t.Parallel()
+	defer discardOutput()()
+
+	opts := testClientGenOpts()
+	opts.Spec = filepath.Join("..", "fixtures", "bugs", "2590", "2590.yaml")
+
+	cwd, err := os.Getwd()
+	require.NoError(t, err)
+	tft, err := os.MkdirTemp(cwd, "generated")
+	require.NoError(t, err)
+	opts.Target = tft
+
+	t.Cleanup(func() {
+		_ = os.RemoveAll(tft)
+	})
+
+	require.NoError(t,
+		GenerateClient("client", []string{}, []string{}, opts),
+	)
+
+	fixtureConfig := map[string][]string{
+		"client/abc/create_responses.go": { // generated file
+			// expected code lines
+			`payload, _ := json.Marshal(o.Payload)`,
+			`return fmt.Sprintf("[POST /abc][%d] createAccepted  %s", 202, payload)`,
+			`return fmt.Sprintf("[POST /abc][%d] createInternalServerError  %s", 500, payload)`,
+		},
+	}
+
+	for fileToInspect, expectedCode := range fixtureConfig {
+		code, err := os.ReadFile(filepath.Join(opts.Target, filepath.FromSlash(fileToInspect)))
+		require.NoError(t, err)
+
+		for line, codeLine := range expectedCode {
+			if !assertInCode(t, strings.TrimSpace(codeLine), string(code)) {
+				t.Logf("Code expected did not match in codegenfile %s for expected line %d: %q", fileToInspect, line, expectedCode[line])
+			}
+		}
+	}
+}