Skip to content

Commit 8bf46b9

Browse files
authored
Merge pull request #291 from manyminds/custom-meta-in-relation
Custom meta in relation
2 parents b893b94 + 3503254 commit 8bf46b9

File tree

5 files changed

+108
-4
lines changed

5 files changed

+108
-4
lines changed

jsonapi/data_structs.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ func (c *DataContainer) MarshalJSON() ([]byte, error) {
5151

5252
// Link represents a link for return in the document.
5353
type Link struct {
54-
Href string `json:"href"`
55-
Meta map[string]interface{} `json:"meta,omitempty"`
54+
Href string `json:"href"`
55+
Meta Meta `json:"meta,omitempty"`
5656
}
5757

5858
// UnmarshalJSON marshals a string value into the Href field or marshals an
@@ -73,6 +73,7 @@ func (l *Link) UnmarshalJSON(payload []byte) error {
7373
if !ok {
7474
return errors.New(`link object expects a "href" key`)
7575
}
76+
7677
l.Meta, _ = obj["meta"].(map[string]interface{})
7778
return nil
7879
}
@@ -95,6 +96,9 @@ func (l Link) MarshalJSON() ([]byte, error) {
9596
// Links contains a map of custom Link objects as given by an element.
9697
type Links map[string]Link
9798

99+
// Meta contains unstructured metadata
100+
type Meta map[string]interface{}
101+
98102
// Data is a general struct for document data and included data.
99103
type Data struct {
100104
Type string `json:"type"`

jsonapi/data_structs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ var _ = Describe("JSONAPI Struct tests", func() {
195195
It("unmarshals from an object", func() {
196196
expected := Link{
197197
Href: "test link",
198-
Meta: map[string]interface{}{
198+
Meta: Meta{
199199
"test": "data",
200200
},
201201
}

jsonapi/fixtures_test.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,13 +459,51 @@ func (n CustomLinksPost) GetCustomLinks(base string) Links {
459459
"someLink": Link{Href: base + `/someLink`},
460460
"otherLink": Link{
461461
Href: base + `/otherLink`,
462-
Meta: map[string]interface{}{
462+
Meta: Meta{
463463
"method": "GET",
464464
},
465465
},
466466
}
467467
}
468468

469+
type CustomMetaPost struct{}
470+
471+
func (n CustomMetaPost) GetID() string {
472+
return "someID"
473+
}
474+
475+
func (n *CustomMetaPost) SetID(ID string) error {
476+
return nil
477+
}
478+
479+
func (n CustomMetaPost) GetName() string {
480+
return "posts"
481+
}
482+
483+
func (n CustomMetaPost) GetReferences() []Reference {
484+
return []Reference{
485+
{
486+
Type: "users",
487+
Name: "author",
488+
IsNotLoaded: true,
489+
},
490+
}
491+
}
492+
493+
func (n CustomMetaPost) GetReferencedIDs() []ReferenceID {
494+
return nil
495+
}
496+
497+
func (n CustomMetaPost) GetCustomMeta(linkURL string) map[string]Meta {
498+
meta := map[string]Meta{
499+
"author": {
500+
"someMetaKey": "someMetaValue",
501+
"someOtherMetaKey": "someOtherMetaValue",
502+
},
503+
}
504+
return meta
505+
}
506+
469507
type NoRelationshipPosts struct{}
470508

471509
func (n NoRelationshipPosts) GetID() string {

jsonapi/marshal.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ type MarshalCustomLinks interface {
8080
GetCustomLinks(string) Links
8181
}
8282

83+
// The MarshalCustomRelationshipMeta interface can be implemented if the struct should
84+
// want a custom meta in a relationship.
85+
type MarshalCustomRelationshipMeta interface {
86+
MarshalIdentifier
87+
GetCustomMeta(string) map[string]Meta
88+
}
89+
8390
// A ServerInformation implementor can be passed to MarshalWithURLs to generate
8491
// the `self` and `related` urls inside `links`.
8592
type ServerInformation interface {
@@ -240,6 +247,19 @@ func isToMany(relationshipType RelationshipType, name string) bool {
240247
return relationshipType == ToManyRelationship
241248
}
242249

250+
func getMetaForRelation(metaSource MarshalCustomRelationshipMeta, name string, information ServerInformation) map[string]interface{} {
251+
meta := make(map[string]interface{})
252+
base := getLinkBaseURL(metaSource, information)
253+
if metaMap, ok := metaSource.GetCustomMeta(base)[name]; ok {
254+
for k, v := range metaMap {
255+
if _, ok := meta[k]; !ok {
256+
meta[k] = v
257+
}
258+
}
259+
}
260+
return meta
261+
}
262+
243263
func getStructRelationships(relationer MarshalLinkedRelations, information ServerInformation) map[string]Relationship {
244264
referencedIDs := relationer.GetReferencedIDs()
245265
sortedResults := map[string][]ReferenceID{}
@@ -282,9 +302,16 @@ func getStructRelationships(relationer MarshalLinkedRelations, information Serve
282302
// set URLs if necessary
283303
links := getLinksForServerInformation(relationer, name, information)
284304

305+
// get the custom meta for this relationship
306+
var meta map[string]interface{}
307+
if customMetaSource, ok := relationer.(MarshalCustomRelationshipMeta); ok {
308+
meta = getMetaForRelation(customMetaSource, name, information)
309+
}
310+
285311
relationship := Relationship{
286312
Data: &container,
287313
Links: links,
314+
Meta: meta,
288315
}
289316

290317
relationships[name] = relationship
@@ -303,8 +330,16 @@ func getStructRelationships(relationer MarshalLinkedRelations, information Serve
303330
}
304331

305332
links := getLinksForServerInformation(relationer, name, information)
333+
334+
// get the custom meta for this relationship
335+
var meta map[string]interface{}
336+
if customMetaSource, ok := relationer.(MarshalCustomRelationshipMeta); ok {
337+
meta = getMetaForRelation(customMetaSource, name, information)
338+
}
339+
306340
relationship := Relationship{
307341
Links: links,
342+
Meta: meta,
308343
}
309344

310345
// skip relationship data completely if IsNotLoaded is set

jsonapi/marshal_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,33 @@ var _ = Describe("Marshalling", func() {
243243
})
244244
})
245245

246+
Context("When marshaling objects with custom meta", func() {
247+
It("contains the custom meta in the marshaled data", func() {
248+
post := CustomMetaPost{}
249+
i, err := MarshalWithURLs(post, CompleteServerInformation{})
250+
Expect(err).To(BeNil())
251+
Expect(i).To(MatchJSON(`{
252+
"data": {
253+
"type": "posts",
254+
"id": "someID",
255+
"attributes": {},
256+
"relationships": {
257+
"author": {
258+
"links": {
259+
"self": "http://my.domain/v1/posts/someID/relationships/author",
260+
"related": "http://my.domain/v1/posts/someID/author"
261+
},
262+
"meta": {
263+
"someMetaKey": "someMetaValue",
264+
"someOtherMetaKey": "someOtherMetaValue"
265+
}
266+
}
267+
}
268+
}
269+
}`))
270+
})
271+
})
272+
246273
Context("When marshaling compound objects", func() {
247274
It("marshals nested objects", func() {
248275
comment1 := Comment{ID: 1, Text: "First!"}

0 commit comments

Comments
 (0)