From 560b236f690eaacd6765bdeee92d49f4e17a84b6 Mon Sep 17 00:00:00 2001 From: Edvin Lindqvist Date: Thu, 3 Sep 2026 16:43:46 +0200 Subject: [PATCH] [go] read rfc7807 title and detail through their pointers Fixes #20553. formatErrorMessage formatted the field's Interface() with %s. Optional rfc7807 members are generated as *string, and %s on a pointer renders the address, so the text the function exists to surface is replaced by it in the error a caller prints: 400 Bad Request %!s(*string=0x40001de0d0) (%!s(*string=0x40001de0e0)) Both states were wrong, not only the populated one. Every member is optional, so a model carrying just a status has both nil and the old code printed the nils: 400 Bad Request %!s(*string=) (%!s(*string=)) The test it used, field != (reflect.Value{}), also cannot distinguish a member that is absent from one that is present and unset, so of the three states a member can be in only the first was handled. rfc7807Field follows a single level of pointer indirection and returns the empty string for all three "nothing to say" cases: no such field, a nil pointer, or an empty string. The message is then built from the members that have something in them, so an unset detail no longer contributes an empty "()". Non-pointer string members keep their existing output. The one deliberate change is that a member with nothing in it is omitted rather than rendered. It also removes a panic on the unconditional Elem(): an untyped nil ("on zero Value") and a model passed by value ("on struct Value"). Generated code always passes &v, so that is hygiene rather than a reachable defect, and it costs nothing because reading through a pointer member needs the Kind checks anyway. A typed nil pointer was already safe and still is. Samples were updated for the 12 Go clients that carry this function. --- .../src/main/resources/go/client.mustache | 51 +++++++++++++++---- .../echo_api/go-external-refs/client.go | 51 +++++++++++++++---- samples/client/echo_api/go/client.go | 51 +++++++++++++++---- .../client.go | 51 +++++++++++++++---- .../client.go | 51 +++++++++++++++---- .../others/go/oneof-anyof-required/client.go | 51 +++++++++++++++---- .../go/oneof-discriminator-lookup/client.go | 51 +++++++++++++++---- .../client/petstore/go/go-petstore/client.go | 51 +++++++++++++++---- .../x-auth-id-alias/go-experimental/client.go | 51 +++++++++++++++---- .../client.go | 51 +++++++++++++++---- .../petstore/go-petstore-withXml/client.go | 51 +++++++++++++++---- .../go/go-petstore-aws-signature/client.go | 51 +++++++++++++++---- .../client/petstore/go/go-petstore/client.go | 51 +++++++++++++++---- 13 files changed, 533 insertions(+), 130 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/go/client.mustache b/modules/openapi-generator/src/main/resources/go/client.mustache index c0fea7cafc3d..8f8b4b69766f 100644 --- a/modules/openapi-generator/src/main/resources/go/client.mustache +++ b/modules/openapi-generator/src/main/resources/go/client.mustache @@ -753,20 +753,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/echo_api/go-external-refs/client.go b/samples/client/echo_api/go-external-refs/client.go index 83d93205607b..5575e9bccbc9 100644 --- a/samples/client/echo_api/go-external-refs/client.go +++ b/samples/client/echo_api/go-external-refs/client.go @@ -669,20 +669,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/echo_api/go/client.go b/samples/client/echo_api/go/client.go index 83d93205607b..5575e9bccbc9 100644 --- a/samples/client/echo_api/go/client.go +++ b/samples/client/echo_api/go/client.go @@ -669,20 +669,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go b/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go index 7a19a74088d8..4ad15f47dd02 100644 --- a/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go +++ b/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go @@ -640,20 +640,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/others/go/issue_20079_go_regex_wrongly_translated/client.go b/samples/client/others/go/issue_20079_go_regex_wrongly_translated/client.go index b681437f098b..2611609bffca 100644 --- a/samples/client/others/go/issue_20079_go_regex_wrongly_translated/client.go +++ b/samples/client/others/go/issue_20079_go_regex_wrongly_translated/client.go @@ -643,20 +643,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/others/go/oneof-anyof-required/client.go b/samples/client/others/go/oneof-anyof-required/client.go index 7a19a74088d8..4ad15f47dd02 100644 --- a/samples/client/others/go/oneof-anyof-required/client.go +++ b/samples/client/others/go/oneof-anyof-required/client.go @@ -640,20 +640,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/others/go/oneof-discriminator-lookup/client.go b/samples/client/others/go/oneof-discriminator-lookup/client.go index 7a19a74088d8..4ad15f47dd02 100644 --- a/samples/client/others/go/oneof-discriminator-lookup/client.go +++ b/samples/client/others/go/oneof-discriminator-lookup/client.go @@ -640,20 +640,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/client/petstore/go/go-petstore/client.go b/samples/client/petstore/go/go-petstore/client.go index 85036942f54b..462f51fc05d6 100644 --- a/samples/client/petstore/go/go-petstore/client.go +++ b/samples/client/petstore/go/go-petstore/client.go @@ -675,20 +675,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go b/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go index 58b26ebc1573..abfa1b852404 100644 --- a/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go +++ b/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go @@ -643,20 +643,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go b/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go index 7f611c52fa4d..509373a8f51e 100644 --- a/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go +++ b/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go @@ -655,20 +655,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/openapi3/client/petstore/go-petstore-withXml/client.go b/samples/openapi3/client/petstore/go-petstore-withXml/client.go index 7f611c52fa4d..509373a8f51e 100644 --- a/samples/openapi3/client/petstore/go-petstore-withXml/client.go +++ b/samples/openapi3/client/petstore/go-petstore-withXml/client.go @@ -655,20 +655,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go b/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go index 73c66a0e3188..b4d1531b3348 100644 --- a/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go +++ b/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go @@ -697,20 +697,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() } diff --git a/samples/openapi3/client/petstore/go/go-petstore/client.go b/samples/openapi3/client/petstore/go/go-petstore/client.go index 8f341a904a36..efa5216f451f 100644 --- a/samples/openapi3/client/petstore/go/go-petstore/client.go +++ b/samples/openapi3/client/petstore/go/go-petstore/client.go @@ -693,20 +693,51 @@ func (e GenericOpenAPIError) Model() interface{} { // format error message using title and detail when model implements rfc7807 func formatErrorMessage(status string, v interface{}) string { - str := "" - metaValue := reflect.ValueOf(v).Elem() + title := rfc7807Field(v, "Title") + detail := rfc7807Field(v, "Detail") + + var str string + switch { + case title != "" && detail != "": + str = title + " (" + detail + ")" + case title != "": + str = title + default: + str = detail + } + + return strings.TrimSpace(status + " " + str) +} - if metaValue.Kind() == reflect.Struct { - field := metaValue.FieldByName("Title") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s", field.Interface()) +// rfc7807Field reads a string member of an rfc7807 model, following one level of pointer +// indirection so that an optional field declared as *string yields its value rather than its +// address. It returns the empty string when the member is absent, is a nil pointer, or holds the +// empty string, so a member that was not set contributes nothing to the message. +func rfc7807Field(v interface{}, name string) string { + value := reflect.ValueOf(v) + if value.Kind() == reflect.Pointer { + if value.IsNil() { + return "" } + value = value.Elem() + } + if value.Kind() != reflect.Struct { + return "" + } - field = metaValue.FieldByName("Detail") - if field != (reflect.Value{}) { - str = fmt.Sprintf("%s (%s)", str, field.Interface()) + field := value.FieldByName(name) + if !field.IsValid() { + return "" + } + if field.Kind() == reflect.Pointer { + if field.IsNil() { + return "" } + field = field.Elem() + } + if field.Kind() != reflect.String { + return "" } - return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) + return field.String() }