Skip to content

Commit f43aa0d

Browse files
committed
Avoid recreation of already created included structs, avoids stackoverflow when two reference eachother
1 parent 0f6f733 commit f43aa0d

File tree

1 file changed

+29
-9
lines changed

1 file changed

+29
-9
lines changed

request.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField refl
6060
return ErrUnsupportedPtrType{rf, t, structField}
6161
}
6262

63+
type includedNode struct {
64+
node *Node
65+
model *reflect.Value
66+
}
67+
6368
// UnmarshalPayload converts an io into a struct instance using jsonapi tags on
6469
// struct fields. This method supports single request payloads only, at the
6570
// moment. Bulk creates and updates are not supported yet.
@@ -94,16 +99,16 @@ func newErrUnsupportedPtrType(rf reflect.Value, t reflect.Type, structField refl
9499
// model interface{} should be a pointer to a struct.
95100
func UnmarshalPayload(in io.Reader, model interface{}) error {
96101
payload := new(OnePayload)
102+
includedMap := make(map[string]*includedNode)
97103

98104
if err := json.NewDecoder(in).Decode(payload); err != nil {
99105
return err
100106
}
101107

102108
if payload.Included != nil {
103-
includedMap := make(map[string]*Node)
104109
for _, included := range payload.Included {
105110
key := fmt.Sprintf("%s,%s", included.Type, included.ID)
106-
includedMap[key] = included
111+
includedMap[key] = &includedNode{included, nil}
107112
}
108113

109114
return unmarshalNode(payload.Data, reflect.ValueOf(model), &includedMap)
@@ -120,13 +125,13 @@ func UnmarshalManyPayload(in io.Reader, t reflect.Type) ([]interface{}, error) {
120125
return nil, err
121126
}
122127

123-
models := []interface{}{} // will be populated from the "data"
124-
includedMap := map[string]*Node{} // will be populate from the "included"
128+
models := []interface{}{} // will be populated from the "data"
129+
includedMap := map[string]*includedNode{} // will be populate from the "included"
125130

126131
if payload.Included != nil {
127132
for _, included := range payload.Included {
128133
key := fmt.Sprintf("%s,%s", included.Type, included.ID)
129-
includedMap[key] = included
134+
includedMap[key] = &includedNode{included, nil}
130135
}
131136
}
132137

@@ -263,7 +268,7 @@ func getStructTags(field reflect.StructField) ([]string, error) {
263268

264269
// unmarshalNodeMaybeChoice populates a model that may or may not be
265270
// a choice type struct that corresponds to a polyrelation or relation
266-
func unmarshalNodeMaybeChoice(m *reflect.Value, data *Node, annotation string, choiceTypeMapping map[string]structFieldIndex, included *map[string]*Node) error {
271+
func unmarshalNodeMaybeChoice(m *reflect.Value, data *Node, annotation string, choiceTypeMapping map[string]structFieldIndex, included *map[string]*includedNode) error {
267272
// This will hold either the value of the choice type model or the actual
268273
// model, depending on annotation
269274
var actualModel = *m
@@ -300,7 +305,7 @@ func unmarshalNodeMaybeChoice(m *reflect.Value, data *Node, annotation string, c
300305
return nil
301306
}
302307

303-
func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node) (err error) {
308+
func unmarshalNode(data *Node, model reflect.Value, included *map[string]*includedNode) (err error) {
304309
defer func() {
305310
if r := recover(); r != nil {
306311
err = fmt.Errorf("data is not a jsonapi representation of '%v'", model.Type())
@@ -509,6 +514,21 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
509514
// model, depending on annotation
510515
m := reflect.New(fieldValue.Type().Elem())
511516

517+
includedKey := fmt.Sprintf("%s,%s", relationship.Data.Type, relationship.Data.ID)
518+
if included != nil && (*included)[includedKey] != nil {
519+
if (*included)[includedKey].model != nil {
520+
fieldValue.Set(*(*included)[includedKey].model)
521+
} else {
522+
(*included)[includedKey].model = &m
523+
err := unmarshalNodeMaybeChoice(&m, (*included)[includedKey].node, annotation, choiceMapping, included)
524+
if err != nil {
525+
er = err
526+
break
527+
}
528+
fieldValue.Set(m)
529+
}
530+
continue
531+
}
512532
err = unmarshalNodeMaybeChoice(&m, relationship.Data, annotation, choiceMapping, included)
513533
if err != nil {
514534
er = err
@@ -565,11 +585,11 @@ func unmarshalNode(data *Node, model reflect.Value, included *map[string]*Node)
565585
return er
566586
}
567587

568-
func fullNode(n *Node, included *map[string]*Node) *Node {
588+
func fullNode(n *Node, included *map[string]*includedNode) *Node {
569589
includedKey := fmt.Sprintf("%s,%s", n.Type, n.ID)
570590

571591
if included != nil && (*included)[includedKey] != nil {
572-
return (*included)[includedKey]
592+
return (*included)[includedKey].node
573593
}
574594

575595
return n

0 commit comments

Comments
 (0)