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

fix: fix apijson.Port for embedded structs #431

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 24 additions & 13 deletions internal/apijson/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,33 @@ func Port(from any, to any) error {
fromJSON := fromVal.FieldByName("JSON")
toJSON := toVal.FieldByName("JSON")

// First, iterate through the from fields and load all the "normal" fields in the struct to the map of
// string to reflect.Value, as well as their raw .JSON.Foo counterpart.
for i := 0; i < fromType.NumField(); i++ {
field := fromType.Field(i)
ptag, ok := parseJSONStructTag(field)
if !ok {
continue
}
if ptag.name == "-" {
continue
// Iterate through the fields of v and load all the "normal" fields in the struct to the map of
// string to reflect.Value, as well as their raw .JSON.Foo counterpart indicated by j.
var getFields func(t reflect.Type, v, j reflect.Value)
getFields = func(t reflect.Type, v, j reflect.Value) {
// Recurse into anonymous fields first, since the fields on the object should win over the fields in the
// embedded object.
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.Anonymous {
getFields(field.Type, v.Field(i), v.FieldByName("JSON"))
continue
}
}
values[ptag.name] = fromVal.Field(i)
if fromJSON.IsValid() {
fields[ptag.name] = fromJSON.FieldByName(field.Name)

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
ptag, ok := parseJSONStructTag(field)
if !ok || ptag.name == "-" {
continue
}
values[ptag.name] = v.Field(i)
if j.IsValid() {
fields[ptag.name] = j.FieldByName(field.Name)
}
}
}
getFields(fromType, fromVal, fromJSON)

// Use the values from the previous step to populate the 'to' struct.
for i := 0; i < toType.NumField(); i++ {
Expand Down
76 changes: 76 additions & 0 deletions internal/apijson/port_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,37 @@ type CardMastercardData struct {
Bar int64 `json:"bar"`
}

type CommonFields struct {
Metadata Metadata `json:"metadata"`
Value string `json:"value"`
}

type commonFieldsJSON struct {
Metadata Field
Value Field
ExtraFields map[string]Field
}

type CardEmbedded struct {
CommonFields
Processor CardVisaProcessor `json:"processor"`
Data CardVisaData `json:"data"`
IsFoo bool `json:"is_foo"`

JSON cardEmbeddedJSON
}

type cardEmbeddedJSON struct {
commonFieldsJSON
Processor Field
Data Field
IsFoo Field
ExtraFields map[string]Field
raw string
}

func (r cardEmbeddedJSON) RawJSON() string { return r.raw }

var portTests = map[string]struct {
from any
to any
Expand Down Expand Up @@ -158,6 +189,51 @@ var portTests = map[string]struct {
Value: false,
},
},
"embedded to card": {
CardEmbedded{
CommonFields: CommonFields{
Metadata: Metadata{
CreatedAt: "Mar 29 2024",
},
Value: "embedded_value",
},
Processor: "visa",
IsFoo: true,
Data: CardVisaData{
Foo: "embedded_foo",
},
JSON: cardEmbeddedJSON{
commonFieldsJSON: commonFieldsJSON{
Metadata: Field{raw: `{"created_at":"Mar 29 2024"}`, status: valid},
Value: Field{raw: `"embedded_value"`, status: valid},
},
raw: `{"processor":"visa","is_foo":true,"data":{"foo":"embedded_foo"}}`,
Processor: Field{raw: `"visa"`, status: valid},
IsFoo: Field{raw: `true`, status: valid},
Data: Field{raw: `{"foo":"embedded_foo"}`, status: valid},
},
},
Card{
Processor: "visa",
IsFoo: true,
IsBar: false,
Data: CardVisaData{
Foo: "embedded_foo",
},
Metadata: Metadata{
CreatedAt: "Mar 29 2024",
},
Value: "embedded_value",
JSON: cardJSON{
raw: "{\"processor\":\"visa\",\"is_foo\":true,\"data\":{\"foo\":\"embedded_foo\"}}",
Processor: Field{raw: `"visa"`, status: 0x3},
IsFoo: Field{raw: "true", status: 0x3},
Data: Field{raw: `{"foo":"embedded_foo"}`, status: 0x3},
Metadata: Field{raw: `{"created_at":"Mar 29 2024"}`, status: 0x3},
Value: Field{raw: `"embedded_value"`, status: 0x3},
},
},
},
}

func TestPort(t *testing.T) {
Expand Down