Skip to content

Commit

Permalink
Add support for unmarshalling top-level links (#56)
Browse files Browse the repository at this point in the history
Co-authored-by: David Sevilla <[email protected]>
  • Loading branch information
mmikalsen and DQSevilla authored Jul 31, 2024
1 parent bfd6611 commit 9b970d9
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 1 deletion.
2 changes: 2 additions & 0 deletions jsonapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ var (
articleEmptyArrayWithToplevelMetaBody = `{"data":[],"meta":{"foo":"bar"}}`
articleEmbeddedBody = `{"data":{"type":"articles","id":"1","attributes":{"title":"A","lastModified":"1989-06-15T00:00:00Z"}}}`
commentEmbeddedBody = `{"data":{"id":"1","type":"comments","attributes":{"body":"A"},"relationships":{"author":{"data":{"id":"1","type":"author"}}}}}`
articleAWithTopLevelLink = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"}},"links":{"self":"http://example.com/article/1"}}`
articleAWithTopLevelLinksRelated = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"}},"links":{"related":{"href":"http://example.com/article/1/comments","meta":{"foo":"bar"}}}}`

// articles with relationships bodies
articleRelatedInvalidEmptyRelationshipBody = `{"data":{"id":"1","type":"articles","attributes":{"title":"A"},"relationships":{"author":{}}}}`
Expand Down
16 changes: 15 additions & 1 deletion unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
// It's used to configure the Unmarshaling by decoding optional fields like Meta.
type Unmarshaler struct {
unmarshalMeta bool
unmarshalLinks bool
meta any
links *Link
memberNameValidationMode MemberNameValidationMode
}

Expand All @@ -25,6 +27,14 @@ func UnmarshalMeta(meta any) UnmarshalOption {
}
}

// UnmarshalLinks copies the Document.Links into the given link.
func UnmarshalLinks(link *Link) UnmarshalOption {
return func(m *Unmarshaler) {
m.unmarshalLinks = true
m.links = link
}
}

// UnmarshalSetNameValidation enables a given level of document member name validation.
func UnmarshalSetNameValidation(mode MemberNameValidationMode) UnmarshalOption {
return func(m *Unmarshaler) {
Expand Down Expand Up @@ -101,7 +111,6 @@ func (d *document) unmarshal(v any, m *Unmarshaler) (err error) {
err = d.unmarshalOptionalFields(m)

return

}

func (d *document) unmarshalOptionalFields(m *Unmarshaler) error {
Expand All @@ -121,6 +130,11 @@ func (d *document) unmarshalOptionalFields(m *Unmarshaler) error {
return err
}
}
if m.unmarshalLinks {
if d.Links != nil {
*m.links = *d.Links
}
}
return nil
}

Expand Down
69 changes: 69 additions & 0 deletions unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,75 @@ func TestUnmarshalMeta(t *testing.T) {
}
}

func TestUnmarshalLinks(t *testing.T) {
t.Parallel()

tests := []struct {
description string
do func() (*Link, error)
expected *Link
}{
{
description: "null",
do: func() (*Link, error) {
var (
a []Article
l Link
)
err := Unmarshal([]byte(articlesABBody), &a, UnmarshalLinks(&l))
return &l, err
},
expected: &Link{},
},
{
description: "*Link",
do: func() (*Link, error) {
var (
a Article
l Link
)
err := Unmarshal([]byte(articleAWithTopLevelLink), &a, UnmarshalLinks(&l))
return &l, err
},
expected: &Link{
Self: "http://example.com/article/1",
},
},
{
description: "*Link with related(any) and meta(any)",
do: func() (*Link, error) {
var (
a Article
l Link
)
err := Unmarshal([]byte(articleAWithTopLevelLinksRelated), &a, UnmarshalLinks(&l))
return &l, err
},
expected: &Link{
Related: map[string]interface{}{
"href": "http://example.com/article/1/comments",
"meta": map[string]interface{}{
"foo": "bar",
},
},
},
},
}

for i, tc := range tests {
tc := tc

t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
t.Parallel()
t.Log(tc.description)

actual, err := tc.do()
is.NoError(t, err)
is.Equal(t, tc.expected, actual)
})
}
}

// TestUnmarshalMemberNameValidation collects tests which verify that invalid member names are
// caught during unmarshaling, no matter where they're placed. This test does not exhaustively test
// every possible invalid name.
Expand Down

0 comments on commit 9b970d9

Please sign in to comment.