Skip to content

Commit 9b970d9

Browse files
mmikalsenDQSevilla
andauthored
Add support for unmarshalling top-level links (#56)
Co-authored-by: David Sevilla <[email protected]>
1 parent bfd6611 commit 9b970d9

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

jsonapi_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ var (
163163
articleEmptyArrayWithToplevelMetaBody = `{"data":[],"meta":{"foo":"bar"}}`
164164
articleEmbeddedBody = `{"data":{"type":"articles","id":"1","attributes":{"title":"A","lastModified":"1989-06-15T00:00:00Z"}}}`
165165
commentEmbeddedBody = `{"data":{"id":"1","type":"comments","attributes":{"body":"A"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}}}`
166+
articleAWithTopLevelLink = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"}},"links":{"self":"http://example.com/article/1"}}`
167+
articleAWithTopLevelLinksRelated = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"}},"links":{"related":{"href":"http://example.com/article/1/comments","meta":{"foo":"bar"}}}}`
166168

167169
// articles with relationships bodies
168170
articleRelatedInvalidEmptyRelationshipBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{}}}}`

unmarshal.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
// It's used to configure the Unmarshaling by decoding optional fields like Meta.
1111
type Unmarshaler struct {
1212
unmarshalMeta bool
13+
unmarshalLinks bool
1314
meta any
15+
links *Link
1416
memberNameValidationMode MemberNameValidationMode
1517
}
1618

@@ -25,6 +27,14 @@ func UnmarshalMeta(meta any) UnmarshalOption {
2527
}
2628
}
2729

30+
// UnmarshalLinks copies the Document.Links into the given link.
31+
func UnmarshalLinks(link *Link) UnmarshalOption {
32+
return func(m *Unmarshaler) {
33+
m.unmarshalLinks = true
34+
m.links = link
35+
}
36+
}
37+
2838
// UnmarshalSetNameValidation enables a given level of document member name validation.
2939
func UnmarshalSetNameValidation(mode MemberNameValidationMode) UnmarshalOption {
3040
return func(m *Unmarshaler) {
@@ -101,7 +111,6 @@ func (d *document) unmarshal(v any, m *Unmarshaler) (err error) {
101111
err = d.unmarshalOptionalFields(m)
102112

103113
return
104-
105114
}
106115

107116
func (d *document) unmarshalOptionalFields(m *Unmarshaler) error {
@@ -121,6 +130,11 @@ func (d *document) unmarshalOptionalFields(m *Unmarshaler) error {
121130
return err
122131
}
123132
}
133+
if m.unmarshalLinks {
134+
if d.Links != nil {
135+
*m.links = *d.Links
136+
}
137+
}
124138
return nil
125139
}
126140

unmarshal_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,75 @@ func TestUnmarshalMeta(t *testing.T) {
634634
}
635635
}
636636

637+
func TestUnmarshalLinks(t *testing.T) {
638+
t.Parallel()
639+
640+
tests := []struct {
641+
description string
642+
do func() (*Link, error)
643+
expected *Link
644+
}{
645+
{
646+
description: "null",
647+
do: func() (*Link, error) {
648+
var (
649+
a []Article
650+
l Link
651+
)
652+
err := Unmarshal([]byte(articlesABBody), &a, UnmarshalLinks(&l))
653+
return &l, err
654+
},
655+
expected: &Link{},
656+
},
657+
{
658+
description: "*Link",
659+
do: func() (*Link, error) {
660+
var (
661+
a Article
662+
l Link
663+
)
664+
err := Unmarshal([]byte(articleAWithTopLevelLink), &a, UnmarshalLinks(&l))
665+
return &l, err
666+
},
667+
expected: &Link{
668+
Self: "http://example.com/article/1",
669+
},
670+
},
671+
{
672+
description: "*Link with related(any) and meta(any)",
673+
do: func() (*Link, error) {
674+
var (
675+
a Article
676+
l Link
677+
)
678+
err := Unmarshal([]byte(articleAWithTopLevelLinksRelated), &a, UnmarshalLinks(&l))
679+
return &l, err
680+
},
681+
expected: &Link{
682+
Related: map[string]interface{}{
683+
"href": "http://example.com/article/1/comments",
684+
"meta": map[string]interface{}{
685+
"foo": "bar",
686+
},
687+
},
688+
},
689+
},
690+
}
691+
692+
for i, tc := range tests {
693+
tc := tc
694+
695+
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
696+
t.Parallel()
697+
t.Log(tc.description)
698+
699+
actual, err := tc.do()
700+
is.NoError(t, err)
701+
is.Equal(t, tc.expected, actual)
702+
})
703+
}
704+
}
705+
637706
// TestUnmarshalMemberNameValidation collects tests which verify that invalid member names are
638707
// caught during unmarshaling, no matter where they're placed. This test does not exhaustively test
639708
// every possible invalid name.

0 commit comments

Comments
 (0)