From 527409bad6b581a7c26caba91022e49e79059c80 Mon Sep 17 00:00:00 2001 From: Artem Lobanov Date: Tue, 28 Mar 2023 16:12:23 +0300 Subject: [PATCH 1/4] Add local id support According to upcoming v1.1 of the JSONAPI the document object identification is a combination of three values: id, type and lid (local id). Local id is an id that can be generated when entity was created but not yet persisted to the DB. In the JSONAPI it should allow client to match data which he created and then received back from the server. --- .gitignore | 1 + api.go | 30 +- api_entity_name_test.go | 11 +- api_interfaces_test.go | 56 ++-- api_test.go | 77 +++-- examples/model/model_chocolate.go | 17 +- examples/model/model_user.go | 24 +- examples/storage/storage_chocolate.go | 2 +- go.mod | 13 +- go.sum | 79 ++--- jsonapi/data_structs.go | 11 +- jsonapi/data_structs_test.go | 20 +- jsonapi/fixtures_test.go | 354 ++++++++++++++++------ jsonapi/integration_test.go | 62 ++-- jsonapi/marshal.go | 34 ++- jsonapi/marshal_composition_test.go | 8 +- jsonapi/marshal_different_to_many_test.go | 11 +- jsonapi/marshal_enum_test.go | 15 +- jsonapi/marshal_same_type_test.go | 9 +- jsonapi/marshal_test.go | 154 ++++++++-- jsonapi/unmarshal.go | 21 +- jsonapi/unmarshal_test.go | 194 +++++++++++- 22 files changed, 855 insertions(+), 348 deletions(-) diff --git a/.gitignore b/.gitignore index 8365624..4942eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.o *.a *.so +*.idea # Folders _obj diff --git a/api.go b/api.go index 3fe9a60..3a2724e 100644 --- a/api.go +++ b/api.go @@ -711,9 +711,9 @@ func (res *resource) handleCreate(c APIContexter, w http.ResponseWriter, r *http } if len(prefix) > 0 { - w.Header().Set("Location", "/"+prefix+"/"+res.name+"/"+result.GetID()) + w.Header().Set("Location", "/"+prefix+"/"+res.name+"/"+result.GetID().ID) } else { - w.Header().Set("Location", "/"+res.name+"/"+result.GetID()) + w.Header().Set("Location", "/"+res.name+"/"+result.GetID().ID) } // handle 200 status codes @@ -764,7 +764,7 @@ func (res *resource) handleUpdate(c APIContexter, w http.ResponseWriter, r *http } identifiable, ok := updatingObj.Interface().(jsonapi.MarshalIdentifier) - if !ok || identifiable.GetID() != id { + if !ok || identifiable.GetID().ID != id { conflictError := errors.New("id in the resource does not match servers endpoint") return NewHTTPError(conflictError, conflictError.Error(), http.StatusConflict) } @@ -1231,9 +1231,10 @@ func handleError(err error, w http.ResponseWriter, r *http.Request, contentType func processRelationshipsData(data interface{}, linkName string, target interface{}) error { hasOne, ok := data.(map[string]interface{}) if ok { - hasOneID, ok := hasOne["id"].(string) - if !ok { - return fmt.Errorf("data object must have a field id for %s", linkName) + hasOneID, okID := hasOne["id"].(string) + hasOneLID, okLID := hasOne["lid"].(string) + if !okID && !okLID { + return fmt.Errorf("data object must have a field id or lid for %s", linkName) } target, ok := target.(jsonapi.UnmarshalToOneRelations) @@ -1241,7 +1242,7 @@ func processRelationshipsData(data interface{}, linkName string, target interfac return errors.New("target struct must implement interface UnmarshalToOneRelations") } - err := target.SetToOneReferenceID(linkName, hasOneID) + err := target.SetToOneReferenceID(linkName, &jsonapi.Identifier{ID: hasOneID, LID: hasOneLID}) if err != nil { return err } @@ -1252,7 +1253,7 @@ func processRelationshipsData(data interface{}, linkName string, target interfac return errors.New("target struct must implement interface UnmarshalToOneRelations") } - err := target.SetToOneReferenceID(linkName, "") + err := target.SetToOneReferenceID(linkName, nil) if err != nil { return err } @@ -1267,22 +1268,23 @@ func processRelationshipsData(data interface{}, linkName string, target interfac return errors.New("target struct must implement interface UnmarshalToManyRelations") } - hasManyIDs := []string{} + hasManyRelations := make([]jsonapi.Identifier, 0, len(hasMany)) for _, entry := range hasMany { data, ok := entry.(map[string]interface{}) if !ok { return fmt.Errorf("entry in data array must be an object for %s", linkName) } - dataID, ok := data["id"].(string) - if !ok { - return fmt.Errorf("all data objects must have a field id for %s", linkName) + dataID, okID := data["id"].(string) + dataLID, okLID := data["lid"].(string) + if !okID && !okLID { + return fmt.Errorf("all data objects must have a field id or lid for %s", linkName) } - hasManyIDs = append(hasManyIDs, dataID) + hasManyRelations = append(hasManyRelations, jsonapi.Identifier{ID: dataID, LID: dataLID}) } - err := target.SetToManyReferenceIDs(linkName, hasManyIDs) + err := target.SetToManyReferenceIDs(linkName, hasManyRelations) if err != nil { return err } diff --git a/api_entity_name_test.go b/api_entity_name_test.go index 973c62b..3f86247 100644 --- a/api_entity_name_test.go +++ b/api_entity_name_test.go @@ -1,6 +1,7 @@ package api2go import ( + "github.com/manyminds/api2go/jsonapi" "net/http" "net/http/httptest" "strings" @@ -11,15 +12,17 @@ import ( type BaguetteTaste struct { ID string `json:"-"` + LID string `json:"-"` Taste string `json:"taste"` } -func (s BaguetteTaste) GetID() string { - return s.ID +func (s BaguetteTaste) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: s.ID, LID: s.LID} } -func (s *BaguetteTaste) SetID(ID string) error { - s.ID = ID +func (s *BaguetteTaste) SetID(ID jsonapi.Identifier) error { + s.ID = ID.ID + s.LID = ID.LID return nil } diff --git a/api_interfaces_test.go b/api_interfaces_test.go index ceeb8f1..5347325 100644 --- a/api_interfaces_test.go +++ b/api_interfaces_test.go @@ -14,16 +14,22 @@ import ( type SomeData struct { ID string `json:"-"` + LID string `json:"-"` Data string `json:"data"` CustomerID string `json:"customerId"` } -func (s SomeData) GetID() string { - return s.ID +func (s SomeData) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: s.ID, LID: s.LID} } -func (s *SomeData) SetID(ID string) error { - s.ID = ID +func (s SomeData) GetName() string { + return "some-datas" +} + +func (s *SomeData) SetID(ID jsonapi.Identifier) error { + s.ID = ID.ID + s.LID = ID.LID return nil } @@ -120,22 +126,22 @@ var _ = Describe("Test interface api type casting", func() { }) It("FindAll returns 404 for simple CRUD", func() { - req, err := http.NewRequest("GET", "/v1/someDatas", nil) + req, err := http.NewRequest("GET", "/v1/some-datas", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusNotFound)) }) It("Works for a normal FindOne", func() { - req, err := http.NewRequest("GET", "/v1/someDatas/12345", nil) + req, err := http.NewRequest("GET", "/v1/some-datas/12345", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusOK)) }) It("Post works with lowercase renaming", func() { - reqBody := strings.NewReader(`{"data": {"attributes":{"customerId": "2" }, "type": "someDatas"}}`) - req, err := http.NewRequest("POST", "/v1/someDatas", reqBody) + reqBody := strings.NewReader(`{"data": {"attributes":{"customerId": "2" }, "type": "some-datas"}}`) + req, err := http.NewRequest("POST", "/v1/some-datas", reqBody) Expect(err).To(BeNil()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusCreated)) @@ -161,7 +167,7 @@ var _ = Describe("Test return code behavior", func() { post := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) + req, err := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -193,7 +199,7 @@ var _ = Describe("Test return code behavior", func() { var err HTTPError json.Unmarshal(rec.Body.Bytes(), &err) Expect(err.Errors[0]).To(Equal(Error{ - Title: "invalid status code 418 from resource someDatas for method Create", + Title: "invalid status code 418 from resource some-datas for method Create", Status: strconv.Itoa(http.StatusInternalServerError)})) }) @@ -218,7 +224,7 @@ var _ = Describe("Test return code behavior", func() { patch := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) + req, err := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -250,7 +256,7 @@ var _ = Describe("Test return code behavior", func() { var err HTTPError json.Unmarshal(rec.Body.Bytes(), &err) Expect(err.Errors[0]).To(Equal(Error{ - Title: "invalid status code 418 from resource someDatas for method Update", + Title: "invalid status code 418 from resource some-datas for method Update", Status: strconv.Itoa(http.StatusInternalServerError)})) }) @@ -268,7 +274,7 @@ var _ = Describe("Test return code behavior", func() { Context("Delete", func() { delete := func(ID string) { - req, err := http.NewRequest("DELETE", "/v1/someDatas/"+ID, nil) + req, err := http.NewRequest("DELETE", "/v1/some-datas/"+ID, nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -318,7 +324,7 @@ var _ = Describe("Test partial CRUD implementation : Creator", func() { post := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) + req, err := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -336,17 +342,17 @@ var _ = Describe("Test partial CRUD implementation : Creator", func() { // Test PATCH m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPatch, errPatch := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) + reqPatch, errPatch := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) Expect(errPatch).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPatch) Expect(rec.Code).To(Equal(http.StatusNotFound)) // Test DELETE - reqDelete, errDelete := http.NewRequest("DELETE", "/v1/someDatas/12345", nil) + reqDelete, errDelete := http.NewRequest("DELETE", "/v1/some-datas/12345", nil) Expect(errDelete).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqDelete) Expect(rec.Code).To(Equal(http.StatusNotFound)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusNotFound)) @@ -373,7 +379,7 @@ var _ = Describe("Test partial CRUD implementation : Updater", func() { patch := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) + req, err := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -391,17 +397,17 @@ var _ = Describe("Test partial CRUD implementation : Updater", func() { // Test POST m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPost, errPost := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) + reqPost, errPost := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) Expect(errPost).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPost) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test DELETE - reqDelete, errDelete := http.NewRequest("DELETE", "/v1/someDatas/12345", nil) + reqDelete, errDelete := http.NewRequest("DELETE", "/v1/some-datas/12345", nil) Expect(errDelete).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqDelete) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) @@ -426,7 +432,7 @@ var _ = Describe("Test partial CRUD implementation : Deleter", func() { Context("Delete", func() { delete := func() { - req, err := http.NewRequest("DELETE", "/v1/someDatas/1234", nil) + req, err := http.NewRequest("DELETE", "/v1/some-datas/1234", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -440,17 +446,17 @@ var _ = Describe("Test partial CRUD implementation : Deleter", func() { // Test POST m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPost, errPost := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) + reqPost, errPost := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) Expect(errPost).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPost) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test PATCH - reqPatch, errPatch := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) + reqPatch, errPatch := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) Expect(errPatch).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPatch) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) diff --git a/api_test.go b/api_test.go index 7f2d583..7a5de94 100644 --- a/api_test.go +++ b/api_test.go @@ -37,12 +37,17 @@ func (m *requestURLResolver) SetRequest(r http.Request) { type invalid string -func (i invalid) GetID() string { +func (i invalid) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: "invalid", LID: "invalid"} +} + +func (i invalid) GetName() string { return "invalid" } type Post struct { ID string `json:"-"` + LID string `json:"-"` Title string `json:"title"` Value null.Float `json:"value"` Author *User `json:"-"` @@ -50,12 +55,17 @@ type Post struct { Bananas []Banana `json:"-"` } -func (p Post) GetID() string { - return p.ID +func (p Post) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: p.ID, LID: p.LID} } -func (p *Post) SetID(ID string) error { - p.ID = ID +func (p Post) GetName() string { + return "posts" +} + +func (p *Post) SetID(ID jsonapi.Identifier) error { + p.ID = ID.ID + p.LID = ID.LID return nil } @@ -79,24 +89,24 @@ func (p Post) GetReferences() []jsonapi.Reference { func (p Post) GetReferencedIDs() []jsonapi.ReferenceID { result := []jsonapi.ReferenceID{} if p.Author != nil { - result = append(result, jsonapi.ReferenceID{ID: p.Author.GetID(), Name: "author", Type: "users"}) + result = append(result, jsonapi.ReferenceID{ID: p.Author.GetID().ID, Name: "author", Type: "users"}) } for _, comment := range p.Comments { - result = append(result, jsonapi.ReferenceID{ID: comment.GetID(), Name: "comments", Type: "comments"}) + result = append(result, jsonapi.ReferenceID{ID: comment.GetID().ID, Name: "comments", Type: "comments"}) } for _, banana := range p.Bananas { - result = append(result, jsonapi.ReferenceID{ID: banana.GetID(), Name: "bananas", Type: "bananas"}) + result = append(result, jsonapi.ReferenceID{ID: banana.GetID().ID, Name: "bananas", Type: "bananas"}) } return result } -func (p *Post) SetToOneReferenceID(name, ID string) error { +func (p *Post) SetToOneReferenceID(name string, ID *jsonapi.Identifier) error { if name == "author" { - if ID == "" { + if ID == nil { p.Author = nil } else { - p.Author = &User{ID: ID} + p.Author = &User{ID: ID.ID, LID: ID.LID} } return nil @@ -105,11 +115,11 @@ func (p *Post) SetToOneReferenceID(name, ID string) error { return errors.New("There is no to-one relationship with the name " + name) } -func (p *Post) SetToManyReferenceIDs(name string, IDs []string) error { +func (p *Post) SetToManyReferenceIDs(name string, IDs []jsonapi.Identifier) error { if name == "comments" { comments := []Comment{} for _, ID := range IDs { - comments = append(comments, Comment{ID: ID}) + comments = append(comments, Comment{ID: ID.ID, LID: ID.LID}) } p.Comments = comments @@ -119,7 +129,7 @@ func (p *Post) SetToManyReferenceIDs(name string, IDs []string) error { if name == "bananas" { bananas := []Banana{} for _, ID := range IDs { - bananas = append(bananas, Banana{ID: ID}) + bananas = append(bananas, Banana{ID: ID.ID, LID: ID.LID}) } p.Bananas = bananas @@ -150,7 +160,7 @@ func (p *Post) DeleteToManyIDs(name string, IDs []string) error { for _, ID := range IDs { // find and delete the comment with ID for pos, comment := range p.Comments { - if comment.GetID() == ID { + if comment.GetID().ID == ID { p.Comments = append(p.Comments[:pos], p.Comments[pos+1:]...) } } @@ -161,7 +171,7 @@ func (p *Post) DeleteToManyIDs(name string, IDs []string) error { for _, ID := range IDs { // find and delete the comment with ID for pos, banana := range p.Bananas { - if banana.GetID() == ID { + if banana.GetID().ID == ID { p.Bananas = append(p.Bananas[:pos], p.Bananas[pos+1:]...) } } @@ -187,30 +197,45 @@ func (p Post) GetReferencedStructs() []jsonapi.MarshalIdentifier { type Comment struct { ID string `json:"-"` + LID string `json:"-"` Value string `json:"value"` } -func (c Comment) GetID() string { - return c.ID +func (c Comment) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: c.ID, LID: c.LID} +} + +func (c Comment) GetName() string { + return "comments" } type Banana struct { ID string `jnson:"-"` + LID string `jnson:"-"` Name string } -func (b Banana) GetID() string { - return b.ID +func (b Banana) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: b.ID, LID: b.LID} +} + +func (b Banana) GetName() string { + return "bananas" } type User struct { ID string `json:"-"` + LID string `json:"-"` Name string `json:"name"` Info string `json:"info"` } -func (u User) GetID() string { - return u.ID +func (u User) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: u.ID, LID: u.LID} +} + +func (u User) GetName() string { + return "users" } type fixtureSource struct { @@ -934,7 +959,7 @@ var _ = Describe("RestHandler", func() { } } `, "/v1/posts/1", "PATCH") - Expect(target.Author.GetID()).To(Equal("2")) + Expect(target.Author.GetID().ID).To(Equal("2")) }) It("Patch can delete to-one relationships", func() { @@ -975,7 +1000,7 @@ var _ = Describe("RestHandler", func() { } } `, "/v1/posts/1", "PATCH") - Expect(target.Comments[0].GetID()).To(Equal("2")) + Expect(target.Comments[0].GetID().ID).To(Equal("2")) }) It("Patch can delete to-many relationships", func() { @@ -1004,7 +1029,7 @@ var _ = Describe("RestHandler", func() { } }`, "/v1/posts/1/relationships/author", "PATCH") target := source.posts["1"] - Expect(target.Author.GetID()).To(Equal("2")) + Expect(target.Author.GetID().ID).To(Equal("2")) }) It("Relationship PATCH route updates to-many", func() { @@ -1016,7 +1041,7 @@ var _ = Describe("RestHandler", func() { }`, "/v1/posts/1/relationships/comments", "PATCH") target := source.posts["1"] Expect(target.Comments).To(HaveLen(1)) - Expect(target.Comments[0].GetID()).To(Equal("2")) + Expect(target.Comments[0].GetID().ID).To(Equal("2")) }) It("Relationship POST route adds to-many elements", func() { diff --git a/examples/model/model_chocolate.go b/examples/model/model_chocolate.go index 39e74c3..79c24a2 100644 --- a/examples/model/model_chocolate.go +++ b/examples/model/model_chocolate.go @@ -1,19 +1,28 @@ package model +import "github.com/manyminds/api2go/jsonapi" + // Chocolate is the chocolate that a user consumes in order to get fat and happy type Chocolate struct { ID string `json:"-"` + LID string `json:"-"` Name string `json:"name"` Taste string `json:"taste"` } // GetID to satisfy jsonapi.MarshalIdentifier interface -func (c Chocolate) GetID() string { - return c.ID +func (c Chocolate) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: c.ID, LID: c.LID} +} + +// GetName to satisfy jsonapi.MarshalIdentifier interface +func (c Chocolate) GetName() string { + return "chocolates" } // SetID to satisfy jsonapi.UnmarshalIdentifier interface -func (c *Chocolate) SetID(id string) error { - c.ID = id +func (c *Chocolate) SetID(ID jsonapi.Identifier) error { + c.ID = ID.ID + c.LID = ID.LID return nil } diff --git a/examples/model/model_user.go b/examples/model/model_user.go index 46f2702..c788bd5 100644 --- a/examples/model/model_user.go +++ b/examples/model/model_user.go @@ -8,7 +8,8 @@ import ( // User is a generic database user type User struct { - ID string `json:"-"` + ID string `json:"-"` + LID string `json:"-"` //rename the username field to user-name. Username string `json:"user-name"` PasswordHash string `json:"-"` @@ -18,13 +19,19 @@ type User struct { } // GetID to satisfy jsonapi.MarshalIdentifier interface -func (u User) GetID() string { - return u.ID +func (u User) GetID() jsonapi.Identifier { + return jsonapi.Identifier{ID: u.ID, LID: u.LID} +} + +// GetName to satisfy jsonapi.MarshalIdentifier interface +func (u User) GetName() string { + return "users" } // SetID to satisfy jsonapi.UnmarshalIdentifier interface -func (u *User) SetID(id string) error { - u.ID = id +func (u *User) SetID(ID jsonapi.Identifier) error { + u.ID = ID.ID + u.LID = ID.LID return nil } @@ -63,9 +70,12 @@ func (u User) GetReferencedStructs() []jsonapi.MarshalIdentifier { } // SetToManyReferenceIDs sets the sweets reference IDs and satisfies the jsonapi.UnmarshalToManyRelations interface -func (u *User) SetToManyReferenceIDs(name string, IDs []string) error { +func (u *User) SetToManyReferenceIDs(name string, IDs []jsonapi.Identifier) error { if name == "sweets" { - u.ChocolatesIDs = IDs + u.ChocolatesIDs = make([]string, 0, len(IDs)) + for _, id := range IDs { + u.ChocolatesIDs = append(u.ChocolatesIDs, id.ID) + } return nil } diff --git a/examples/storage/storage_chocolate.go b/examples/storage/storage_chocolate.go index 499f487..31bbdb4 100644 --- a/examples/storage/storage_chocolate.go +++ b/examples/storage/storage_chocolate.go @@ -19,7 +19,7 @@ func (c byID) Swap(i, j int) { } func (c byID) Less(i, j int) bool { - return c[i].GetID() < c[j].GetID() + return c[i].GetID().ID < c[j].GetID().ID } // NewChocolateStorage initializes the storage diff --git a/go.mod b/go.mod index a0b26a2..fed870f 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/manyminds/api2go +module github.com/retailnext/api2go go 1.14 @@ -8,14 +8,11 @@ require ( github.com/gorilla/mux v1.7.4 github.com/julienschmidt/httprouter v1.3.0 github.com/labstack/echo v3.3.10+incompatible - github.com/labstack/gommon v0.3.0 // indirect - github.com/mattn/goveralls v0.0.11 // indirect - github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/manyminds/api2go v0.0.0-20220325145637-95b4fb838cf6 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.10.1 - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/mod v0.5.1 // indirect - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect - golang.org/x/tools v0.1.7 // indirect gopkg.in/guregu/null.v3 v3.4.0 ) + +replace github.com/manyminds/api2go v0.0.0-20220325145637-95b4fb838cf6 => ./ diff --git a/go.sum b/go.sum index 912cc74..f3326f0 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -17,10 +18,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -31,11 +30,11 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -43,118 +42,81 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= -github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/goveralls v0.0.11 h1:eJXea6R6IFlL1QMKNMzDvvHv/hwGrnvyig4N+0+XiMM= -github.com/mattn/goveralls v0.0.11/go.mod h1:gU8SyhNswsJKchEV93xRQxX6X3Ei4PJdQk/6ZHvrvRk= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk= -golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -169,7 +131,6 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/guregu/null.v3 v3.4.0 h1:AOpMtZ85uElRhQjEDsFx21BkXqFPwA7uoJukd4KErIs= gopkg.in/guregu/null.v3 v3.4.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= @@ -177,7 +138,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/jsonapi/data_structs.go b/jsonapi/data_structs.go index 279b6cf..a75c115 100644 --- a/jsonapi/data_structs.go +++ b/jsonapi/data_structs.go @@ -117,6 +117,7 @@ type Meta map[string]interface{} type Data struct { Type string `json:"type"` ID string `json:"id"` + LID string `json:"lid,omitempty"` Attributes json.RawMessage `json:"attributes"` Relationships map[string]Relationship `json:"relationships,omitempty"` Links Links `json:"links,omitempty"` @@ -133,8 +134,8 @@ type Relationship struct { // A RelationshipDataContainer is used to marshal and unmarshal single relationship // objects and arrays of relationship objects. type RelationshipDataContainer struct { - DataObject *RelationshipData - DataArray []RelationshipData + DataObject *Identifier + DataArray []Identifier } // UnmarshalJSON unmarshals the JSON-encoded data to the DataObject field if the @@ -161,9 +162,3 @@ func (c *RelationshipDataContainer) MarshalJSON() ([]byte, error) { } return json.Marshal(c.DataObject) } - -// RelationshipData represents one specific reference ID. -type RelationshipData struct { - Type string `json:"type"` - ID string `json:"id"` -} diff --git a/jsonapi/data_structs_test.go b/jsonapi/data_structs_test.go index 4ce2337..072c3a0 100644 --- a/jsonapi/data_structs_test.go +++ b/jsonapi/data_structs_test.go @@ -14,6 +14,7 @@ var _ = Describe("JSONAPI Struct tests", func() { "data": { "type": "test", "id": "1", + "lid": "2", "attributes": {"foo": "bar"}, "relationships": { "author": { @@ -26,12 +27,13 @@ var _ = Describe("JSONAPI Struct tests", func() { expectedData := &Data{ Type: "test", ID: "1", + LID: "2", Attributes: json.RawMessage([]byte(`{"foo": "bar"}`)), Relationships: map[string]Relationship{ "author": { Data: &RelationshipDataContainer{ - DataObject: &RelationshipData{ - Type: "author", + DataObject: &Identifier{ + Name: "author", ID: "1", }, }, @@ -56,8 +58,8 @@ var _ = Describe("JSONAPI Struct tests", func() { "relationships": { "comments": { "data": [ - {"type": "comments", "id": "1"}, - {"type": "comments", "id": "2"} + {"type": "comments", "id": "1", "lid": "3"}, + {"type": "comments", "id": "2", "lid": "4"} ] } } @@ -72,14 +74,16 @@ var _ = Describe("JSONAPI Struct tests", func() { Relationships: map[string]Relationship{ "comments": { Data: &RelationshipDataContainer{ - DataArray: []RelationshipData{ + DataArray: []Identifier{ { - Type: "comments", + Name: "comments", ID: "1", + LID: "3", }, { - Type: "comments", + Name: "comments", ID: "2", + LID: "4", }, }, }, @@ -145,7 +149,7 @@ var _ = Describe("JSONAPI Struct tests", func() { Relationships: map[string]Relationship{ "comments": { Data: &RelationshipDataContainer{ - DataArray: []RelationshipData{}, + DataArray: []Identifier{}, }, }, "author": { diff --git a/jsonapi/fixtures_test.go b/jsonapi/fixtures_test.go index 52f3008..23c464c 100644 --- a/jsonapi/fixtures_test.go +++ b/jsonapi/fixtures_test.go @@ -15,8 +15,12 @@ type Magic struct { ID MagicID `json:"-"` } -func (m Magic) GetID() string { - return m.ID.String() +func (m Magic) GetID() Identifier { + return Identifier{ID: m.ID.String()} +} + +func (m Magic) GetName() string { + return "magics" } type MagicID string @@ -27,22 +31,39 @@ func (m MagicID) String() string { type Comment struct { ID int `json:"-"` + LID int `json:"-"` Text string `json:"text"` SubComments []Comment `json:"-"` SubCommentsEmpty bool `json:"-"` } -func (c Comment) GetID() string { - return fmt.Sprintf("%d", c.ID) +func (c Comment) GetID() Identifier { + id := Identifier{ID: fmt.Sprintf("%d", c.ID)} + if c.LID != 0 { + id.LID = fmt.Sprintf("%d", c.LID) + } + return id } -func (c *Comment) SetID(stringID string) error { - id, err := strconv.Atoi(stringID) - if err != nil { - return err - } +func (c Comment) GetName() string { + return "comments" +} - c.ID = id +func (c *Comment) SetID(ID Identifier) error { + if ID.ID != "" { + id, err := strconv.Atoi(ID.ID) + if err != nil { + return err + } + c.ID = id + } + if ID.LID != "" { + lid, err := strconv.Atoi(ID.LID) + if err != nil { + return err + } + c.LID = lid + } return nil } @@ -61,7 +82,8 @@ func (c Comment) GetReferencedIDs() []ReferenceID { result := []ReferenceID{} for _, comment := range c.SubComments { - commentID := ReferenceID{Type: "comments", Name: "comments", ID: comment.GetID()} + id := comment.GetID() + commentID := ReferenceID{Type: "comments", Name: "comments", ID: id.ID, LID: id.LID} result = append(result, commentID) } @@ -80,27 +102,41 @@ func (c Comment) GetReferencedStructs() []MarshalIdentifier { type User struct { ID int `json:"-"` + LID int `json:"-"` Name string `json:"name"` Password string `json:"-"` } -func (u User) GetID() string { - return fmt.Sprintf("%d", u.ID) +func (u User) GetID() Identifier { + id := Identifier{ID: fmt.Sprintf("%d", u.ID)} + if u.LID != 0 { + id.LID = fmt.Sprintf("%d", u.LID) + } + return id +} + +func (u User) GetName() string { + return "users" } -func (u *User) SetID(stringID string) error { - id, err := strconv.Atoi(stringID) +func (u *User) SetID(ID Identifier) error { + id, err := strconv.Atoi(ID.ID) if err != nil { return err } - u.ID = id + lid, err := strconv.Atoi(ID.LID) + if err != nil { + return err + } + u.LID = lid return nil } type SimplePost struct { ID string `json:"-"` + LID string `json:"-"` Title string `json:"title"` Text string `json:"text"` Internal string `json:"-"` @@ -110,12 +146,22 @@ type SimplePost struct { topSecret string } -func (s SimplePost) GetID() string { - return s.ID +func (s SimplePost) GetID() Identifier { + return Identifier{ID: s.ID, LID: s.LID} +} + +func (s SimplePost) GetName() string { + return "simple-posts" +} + +func (s *SimplePost) SetID(ID Identifier) error { + s.ID = ID.ID + s.LID = ID.LID + return nil } -func (s *SimplePost) SetID(ID string) error { - s.ID = ID +func (s *SimplePost) SetLID(ID string) error { + s.LID = ID return nil } @@ -124,37 +170,69 @@ type ErrorIDPost struct { Error error } -func (s ErrorIDPost) GetID() string { - return "" +func (s ErrorIDPost) GetID() Identifier { + return Identifier{ID: "", LID: ""} } -func (s *ErrorIDPost) SetID(ID string) error { +func (s *ErrorIDPost) SetID(ID Identifier) error { return s.Error } type Post struct { ID int `json:"-"` + LID int `json:"-"` Title string `json:"title"` Comments []Comment `json:"-"` CommentsIDs []int `json:"-"` + CommentsLIDs []int `json:"-"` CommentsEmpty bool `json:"-"` Author *User `json:"-"` AuthorID sql.NullInt64 `json:"-"` + AuthorLID sql.NullInt64 `json:"-"` AuthorEmpty bool `json:"-"` } -func (c Post) GetID() string { - return fmt.Sprintf("%d", c.ID) +func (c Post) GetID() Identifier { + id := Identifier{ID: fmt.Sprintf("%d", c.ID)} + if c.LID != 0 { + id.LID = fmt.Sprintf("%d", c.LID) + } + return id +} + +func (c Post) GetName() string { + return "posts" } -func (c *Post) SetID(stringID string) error { - id, err := strconv.Atoi(stringID) +func (c *Post) SetID(ID Identifier) error { + if ID.ID != "" { + id, err := strconv.Atoi(ID.ID) + if err != nil { + return err + } + c.ID = id + } + if ID.LID != "" { + lid, err := strconv.Atoi(ID.LID) + if err != nil { + return err + } + c.LID = lid + } + + return nil +} + +func (c *Post) SetLID(stringID string) error { + if stringID == "" { + return nil + } + var err error + c.LID, err = strconv.Atoi(stringID) if err != nil { return err } - c.ID = id - return nil } @@ -173,15 +251,24 @@ func (c Post) GetReferences() []Reference { } } -func (c *Post) SetToOneReferenceID(name, ID string) error { +func (c *Post) SetToOneReferenceID(name string, ID *Identifier) error { if name == "author" { // Ignore empty author relationships - if ID != "" { - intID, err := strconv.ParseInt(ID, 10, 64) - if err != nil { - return err + if ID != nil { + if ID.ID != "" { + intID, err := strconv.ParseInt(ID.ID, 10, 64) + if err != nil { + return err + } + c.AuthorID = sql.NullInt64{Valid: true, Int64: intID} + } + if ID.LID != "" { + intLID, err := strconv.ParseInt(ID.LID, 10, 64) + if err != nil { + return err + } + c.AuthorLID = sql.NullInt64{Valid: true, Int64: intLID} } - c.AuthorID = sql.NullInt64{Valid: true, Int64: intID} } return nil @@ -190,20 +277,32 @@ func (c *Post) SetToOneReferenceID(name, ID string) error { return errors.New("There is no to-one relationship named " + name) } -func (c *Post) SetToManyReferenceIDs(name string, IDs []string) error { +func (c *Post) SetToManyReferenceIDs(name string, IDs []Identifier) error { if name == "comments" { - commentsIDs := []int{} + var commentsIDs []int + var commentsLIDs []int for _, ID := range IDs { - intID, err := strconv.ParseInt(ID, 10, 64) - if err != nil { - return err + if ID.ID != "" { + intID, err := strconv.ParseInt(ID.ID, 10, 64) + if err != nil { + return err + } + + commentsIDs = append(commentsIDs, int(intID)) } + if ID.LID != "" { + intLID, err := strconv.ParseInt(ID.LID, 10, 64) + if err != nil { + return err + } - commentsIDs = append(commentsIDs, int(intID)) + commentsLIDs = append(commentsLIDs, int(intLID)) + } } c.CommentsIDs = commentsIDs + c.CommentsLIDs = commentsLIDs return nil } @@ -233,7 +332,8 @@ func (c Post) GetReferencedIDs() []ReferenceID { result := []ReferenceID{} if c.Author != nil { - authorID := ReferenceID{Type: "users", Name: "author", ID: c.Author.GetID()} + id := c.Author.GetID() + authorID := ReferenceID{Type: "users", Name: "author", ID: id.ID, LID: id.LID} result = append(result, authorID) } else if c.AuthorID.Valid { authorID := ReferenceID{Type: "users", Name: "author", ID: fmt.Sprintf("%d", c.AuthorID.Int64)} @@ -242,7 +342,8 @@ func (c Post) GetReferencedIDs() []ReferenceID { if len(c.Comments) > 0 { for _, comment := range c.Comments { - result = append(result, ReferenceID{Type: "comments", Name: "comments", ID: comment.GetID()}) + id := comment.GetID() + result = append(result, ReferenceID{Type: "comments", Name: "comments", ID: id.ID, LID: id.LID}) } } else if len(c.CommentsIDs) > 0 { for _, commentID := range c.CommentsIDs { @@ -273,12 +374,21 @@ func (c *Post) SetReferencedStructs(references []UnmarshalIdentifier) error { type AnotherPost struct { ID int `json:"-"` + LID int `json:"-"` AuthorID int `json:"-"` Author *User `json:"-"` } -func (p AnotherPost) GetID() string { - return fmt.Sprintf("%d", p.ID) +func (p AnotherPost) GetID() Identifier { + id := Identifier{ID: fmt.Sprintf("%d", p.ID)} + if p.LID != 0 { + id.LID = fmt.Sprintf("%d", p.LID) + } + return id +} + +func (p AnotherPost) GetName() string { + return "another-posts" } func (p AnotherPost) GetReferences() []Reference { @@ -302,22 +412,32 @@ func (p AnotherPost) GetReferencedIDs() []ReferenceID { type ZeroPost struct { ID string `json:"-"` + LID string `json:"-"` Title string `json:"title"` Value zero.Float `json:"value"` } -func (z ZeroPost) GetID() string { - return z.ID +func (z ZeroPost) GetID() Identifier { + return Identifier{ID: z.ID, LID: z.LID} +} + +func (z ZeroPost) GetName() string { + return "zero-posts" } type ZeroPostPointer struct { ID string `json:"-"` + LID string `json:"-"` Title string `json:"title"` Value *zero.Float `json:"value"` } -func (z ZeroPostPointer) GetID() string { - return z.ID +func (z ZeroPostPointer) GetID() Identifier { + return Identifier{ID: z.ID, LID: z.LID} +} + +func (z ZeroPostPointer) GetName() string { + return "zero-post-pointers" } type Question struct { @@ -327,8 +447,12 @@ type Question struct { InspiringQuestion *Question `json:"-"` } -func (q Question) GetID() string { - return q.ID +func (q Question) GetID() Identifier { + return Identifier{ID: q.ID, LID: ""} +} + +func (q Question) GetName() string { + return "questions" } func (q Question) GetReferences() []Reference { @@ -362,17 +486,39 @@ func (q Question) GetReferencedStructs() []MarshalIdentifier { type Identity struct { ID int64 `json:"-"` + LID int64 `json:"-"` Scopes []string `json:"scopes"` } -func (i Identity) GetID() string { - return fmt.Sprintf("%d", i.ID) +func (i Identity) GetID() Identifier { + id := Identifier{ID: fmt.Sprintf("%d", i.ID)} + if i.LID != 0 { + id.LID = fmt.Sprintf("%d", i.LID) + } + return id } -func (i *Identity) SetID(ID string) error { - var err error - i.ID, err = strconv.ParseInt(ID, 10, 64) - return err +func (i Identity) GetName() string { + return "identities" +} + +func (i *Identity) SetID(ID Identifier) error { + if ID.ID != "" { + id, err := strconv.Atoi(ID.ID) + if err != nil { + return err + } + i.ID = int64(id) + } + if ID.LID != "" { + lid, err := strconv.Atoi(ID.LID) + if err != nil { + return err + } + i.LID = int64(lid) + } + + return nil } type Unicorn struct { @@ -380,24 +526,31 @@ type Unicorn struct { Scopes []string `json:"scopes"` } -func (u Unicorn) GetID() string { - return "magicalUnicorn" +func (u Unicorn) GetID() Identifier { + return Identifier{ID: "magicalUnicorn", LID: ""} +} + +func (u Unicorn) GetName() string { + return "unicorns" } type NumberPost struct { ID string `json:"-"` + LID string `json:"-"` Title string Number int64 UnsignedNumber uint64 } -func (n *NumberPost) SetID(ID string) error { - n.ID = ID +func (n *NumberPost) SetID(ID Identifier) error { + n.ID = ID.ID + n.LID = ID.LID return nil } type SQLNullPost struct { ID string `json:"-"` + LID string `json:"-"` Title zero.String `json:"title"` Likes zero.Int `json:"likes"` Rating zero.Float `json:"rating"` @@ -405,26 +558,29 @@ type SQLNullPost struct { Today zero.Time `json:"today"` } -func (s SQLNullPost) GetID() string { - return s.ID +func (s SQLNullPost) GetID() Identifier { + return Identifier{ID: s.ID, LID: s.LID} } -func (s *SQLNullPost) SetID(ID string) error { - s.ID = ID +func (s *SQLNullPost) SetID(ID Identifier) error { + s.ID = ID.ID + s.LID = ID.LID return nil } type RenamedPostWithEmbedding struct { Embedded SQLNullPost ID string `json:"-"` + LID string `json:"-"` Another string `json:"another"` Field string `json:"foo"` Other string `json:"bar-bar"` Ignored string `json:"-"` } -func (p *RenamedPostWithEmbedding) SetID(ID string) error { - p.ID = ID +func (p *RenamedPostWithEmbedding) SetID(ID Identifier) error { + p.ID = ID.ID + p.LID = ID.LID return nil } @@ -436,8 +592,8 @@ type RenamedComment struct { Data string } -func (r RenamedComment) GetID() string { - return "666" +func (r RenamedComment) GetID() Identifier { + return Identifier{ID: "666", LID: ""} } func (r RenamedComment) GetName() string { @@ -479,11 +635,11 @@ func (i PrefixServerInformation) GetPrefix() string { type CustomLinksPost struct{} -func (n CustomLinksPost) GetID() string { - return "someID" +func (n CustomLinksPost) GetID() Identifier { + return Identifier{ID: "someID", LID: ""} } -func (n *CustomLinksPost) SetID(ID string) error { +func (n *CustomLinksPost) SetID(ID Identifier) error { return nil } @@ -506,11 +662,11 @@ func (n CustomLinksPost) GetCustomLinks(base string) Links { type CustomResourceMetaPost struct{} -func (n CustomResourceMetaPost) GetID() string { - return "someID" +func (n CustomResourceMetaPost) GetID() Identifier { + return Identifier{ID: "someID", LID: ""} } -func (n *CustomResourceMetaPost) SetID(ID string) error { +func (n *CustomResourceMetaPost) SetID(ID Identifier) error { return nil } @@ -524,11 +680,11 @@ func (n CustomResourceMetaPost) Meta() Meta { type CustomMetaPost struct{} -func (n CustomMetaPost) GetID() string { - return "someID" +func (n CustomMetaPost) GetID() Identifier { + return Identifier{ID: "someID", LID: ""} } -func (n *CustomMetaPost) SetID(ID string) error { +func (n *CustomMetaPost) SetID(ID Identifier) error { return nil } @@ -562,11 +718,11 @@ func (n CustomMetaPost) GetCustomMeta(linkURL string) map[string]Meta { type NoRelationshipPosts struct{} -func (n NoRelationshipPosts) GetID() string { - return "someID" +func (n NoRelationshipPosts) GetID() Identifier { + return Identifier{ID: "someID", LID: ""} } -func (n *NoRelationshipPosts) SetID(ID string) error { +func (n *NoRelationshipPosts) SetID(ID Identifier) error { return nil } @@ -576,11 +732,11 @@ func (n NoRelationshipPosts) GetName() string { type ErrorRelationshipPosts struct{} -func (e ErrorRelationshipPosts) GetID() string { - return "errorID" +func (e ErrorRelationshipPosts) GetID() Identifier { + return Identifier{ID: "errorID", LID: ""} } -func (e *ErrorRelationshipPosts) SetID(ID string) error { +func (e *ErrorRelationshipPosts) SetID(ID Identifier) error { return nil } @@ -588,25 +744,31 @@ func (e ErrorRelationshipPosts) GetName() string { return "posts" } -func (e ErrorRelationshipPosts) SetToOneReferenceID(name, ID string) error { +func (e ErrorRelationshipPosts) SetToOneReferenceID(name string, ID *Identifier) error { return errors.New("this never works") } -func (e ErrorRelationshipPosts) SetToManyReferenceIDs(name string, IDs []string) error { +func (e ErrorRelationshipPosts) SetToManyReferenceIDs(name string, IDs []Identifier) error { return errors.New("this also never works") } type Image struct { ID string `json:"-"` + LID string `json:"-"` Ports []ImagePort `json:"image-ports"` } -func (i Image) GetID() string { - return i.ID +func (i Image) GetID() Identifier { + return Identifier{ID: i.ID, LID: i.LID} +} + +func (i Image) GetName() string { + return "images" } -func (i *Image) SetID(ID string) error { - i.ID = ID +func (i *Image) SetID(ID Identifier) error { + i.ID = ID.ID + i.LID = ID.LID return nil } @@ -622,8 +784,12 @@ type Article struct { Relationship RelationshipType `json:"-"` } -func (a Article) GetID() string { - return "id" +func (a Article) GetID() Identifier { + return Identifier{ID: "id", LID: ""} +} + +func (a Article) GetName() string { + return "articles" } func (a Article) GetReferences() []Reference { @@ -645,8 +811,8 @@ type DeepDedendencies struct { Relationships []DeepDedendencies `json:"-"` } -func (d DeepDedendencies) GetID() string { - return d.ID +func (d DeepDedendencies) GetID() Identifier { + return Identifier{ID: d.ID, LID: ""} } func (DeepDedendencies) GetName() string { diff --git a/jsonapi/integration_test.go b/jsonapi/integration_test.go index 5fafa64..c8f4e23 100644 --- a/jsonapi/integration_test.go +++ b/jsonapi/integration_test.go @@ -8,20 +8,26 @@ import ( ) type Book struct { - ID string `json:"-"` - Author *StupidUser `json:"-"` - AuthorID string `json:"-"` - Pages []Page `json:"-"` - PagesIDs []string `json:"-"` + ID string `json:"-"` + LID string `json:"-"` + Author *StupidUser `json:"-"` + AuthorID string `json:"-"` + AuthorLID string `json:"-"` + Pages []Page `json:"-"` + PagesIDs []string `json:"-"` } -func (b Book) GetID() string { - return b.ID +func (b Book) GetID() Identifier { + return Identifier{ID: b.ID, LID: b.LID} } -func (b *Book) SetID(ID string) error { - b.ID = ID +func (b Book) GetName() string { + return "books" +} +func (b *Book) SetID(ID Identifier) error { + b.ID = ID.ID + b.LID = ID.LID return nil } @@ -42,16 +48,20 @@ func (b Book) GetReferencedIDs() []ReferenceID { result := []ReferenceID{} if b.Author != nil { + id := b.Author.GetID() result = append(result, ReferenceID{ - ID: b.Author.GetID(), + ID: id.ID, + LID: id.LID, Name: "author", Type: "stupidUsers", }) } for _, page := range b.Pages { + id := page.GetID() result = append(result, ReferenceID{ - ID: page.GetID(), + ID: id.ID, + LID: id.LID, Name: "pages", Type: "pages", }) @@ -60,9 +70,10 @@ func (b Book) GetReferencedIDs() []ReferenceID { return result } -func (b *Book) SetToOneReferenceID(name, ID string) error { +func (b *Book) SetToOneReferenceID(name string, ID *Identifier) error { if name == "author" { - b.AuthorID = ID + b.AuthorID = ID.ID + b.AuthorLID = ID.LID return nil } @@ -70,9 +81,12 @@ func (b *Book) SetToOneReferenceID(name, ID string) error { return errors.New("There is no to-one relationship with name " + name) } -func (b *Book) SetToManyReferenceIDs(name string, IDs []string) error { +func (b *Book) SetToManyReferenceIDs(name string, IDs []Identifier) error { if name == "pages" { - b.PagesIDs = IDs + b.PagesIDs = make([]string, 0, len(IDs)) + for _, id := range IDs { + b.PagesIDs = append(b.PagesIDs, id.ID) + } return nil } @@ -99,8 +113,12 @@ type StupidUser struct { Name string `json:"name"` } -func (s StupidUser) GetID() string { - return s.ID +func (s StupidUser) GetID() Identifier { + return Identifier{ID: s.ID, LID: ""} +} + +func (s StupidUser) GetName() string { + return "stupid-users" } type Page struct { @@ -108,8 +126,12 @@ type Page struct { Content string `json:"content"` } -func (p Page) GetID() string { - return p.ID +func (p Page) GetID() Identifier { + return Identifier{ID: p.ID, LID: ""} +} + +func (p Page) GetName() string { + return "pages" } var _ = Describe("Test for the public api of this package", func() { @@ -166,7 +188,7 @@ var _ = Describe("Test for the public api of this package", func() { "attributes": { "name" : "Terry Pratchett" }, - "type" : "stupidUsers" + "type" : "stupid-users" }, { "attributes": { diff --git a/jsonapi/marshal.go b/jsonapi/marshal.go index 6e36ce1..c11f04c 100644 --- a/jsonapi/marshal.go +++ b/jsonapi/marshal.go @@ -25,13 +25,21 @@ const ( // // Note: The implementation of this interface is mandatory. type MarshalIdentifier interface { - GetID() string + GetID() Identifier + EntityNamer +} + +type Identifier struct { + ID string `json:"id"` + LID string `json:"lid,omitempty"` + Name string `json:"type"` } // ReferenceID contains all necessary information in order to reference another // struct in JSON API. type ReferenceID struct { ID string + LID string Type string Name string Relationship RelationshipType @@ -217,7 +225,7 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) alreadyIncluded[structType] = make(map[string]bool) } - if !alreadyIncluded[structType][referencedStruct.GetID()] { + if !alreadyIncluded[structType][referencedStruct.GetID().ID] { var data Data err := marshalData(referencedStruct, &data, information) if err != nil { @@ -225,7 +233,7 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) } includedElements = append(includedElements, data) - alreadyIncluded[structType][referencedStruct.GetID()] = true + alreadyIncluded[structType][referencedStruct.GetID().ID] = true } } @@ -244,7 +252,9 @@ func marshalData(element MarshalIdentifier, data *Data, information ServerInform } data.Attributes = attributes - data.ID = element.GetID() + identifier := element.GetID() + data.ID = identifier.ID + data.LID = identifier.LID data.Type = getStructType(element) if information != nil { @@ -322,17 +332,19 @@ func getStructRelationships(relationer MarshalLinkedRelations, information Serve if isToMany(referenceIDs[0].Relationship, referenceIDs[0].Name) { // multiple elements in links - container.DataArray = []RelationshipData{} + container.DataArray = []Identifier{} for _, referenceID := range referenceIDs { - container.DataArray = append(container.DataArray, RelationshipData{ - Type: referenceID.Type, + container.DataArray = append(container.DataArray, Identifier{ + Name: referenceID.Type, ID: referenceID.ID, + LID: referenceID.LID, }) } } else { - container.DataObject = &RelationshipData{ - Type: referenceIDs[0].Type, + container.DataObject = &Identifier{ + Name: referenceIDs[0].Type, ID: referenceIDs[0].ID, + LID: referenceIDs[0].LID, } } @@ -363,7 +375,7 @@ func getStructRelationships(relationer MarshalLinkedRelations, information Serve // Plural empty relationships need an empty array and empty to-one need a null in the json if !reference.IsNotLoaded && isToMany(reference.Relationship, reference.Name) { - container.DataArray = []RelationshipData{} + container.DataArray = []Identifier{} } links := getLinksForServerInformation(relationer, name, information) @@ -399,7 +411,7 @@ func getLinkBaseURL(element MarshalIdentifier, information ServerInformation) st prefix += "/" + namespace } - return fmt.Sprintf("%s/%s/%s", prefix, structType, element.GetID()) + return fmt.Sprintf("%s/%s/%s", prefix, structType, element.GetID().ID) } func getLinksForServerInformation(relationer MarshalLinkedRelations, name string, information ServerInformation) Links { diff --git a/jsonapi/marshal_composition_test.go b/jsonapi/marshal_composition_test.go index 01a10ff..ca99ea4 100644 --- a/jsonapi/marshal_composition_test.go +++ b/jsonapi/marshal_composition_test.go @@ -12,6 +12,10 @@ type TaggedPost struct { Tag string `json:"tag"` } +func (p TaggedPost) GetName() string { + return "tagged-posts" +} + var _ = Describe("Embedded struct types", func() { created, _ := time.Parse(time.RFC3339, "2014-11-10T16:30:48.823Z") post := TaggedPost{ @@ -25,7 +29,7 @@ var _ = Describe("Embedded struct types", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type":"taggedPosts", + "type":"tagged-posts", "id":"first", "attributes": { "title":"First Post", @@ -43,7 +47,7 @@ var _ = Describe("Embedded struct types", func() { Context("When unmarshaling objects with struct composition", func() { postJSON := `{ "data": { - "type": "taggedPosts", + "type": "tagged-posts", "id": "first", "attributes": { "title": "First Post", diff --git a/jsonapi/marshal_different_to_many_test.go b/jsonapi/marshal_different_to_many_test.go index ae1ecbf..87cdc91 100644 --- a/jsonapi/marshal_different_to_many_test.go +++ b/jsonapi/marshal_different_to_many_test.go @@ -7,11 +7,16 @@ import ( type ManyParent struct { ID string `json:"-"` + LID string `json:"-"` Content string `json:"content"` } -func (m ManyParent) GetID() string { - return m.ID +func (m ManyParent) GetID() Identifier { + return Identifier{ID: m.ID, LID: m.LID} +} + +func (m ManyParent) GetName() string { + return "many-parents" } func (m ManyParent) GetReferences() []Reference { @@ -71,7 +76,7 @@ var _ = Describe("Marshalling toMany relations with the same name and different ] } }, - "type": "manyParents" + "type": "many-parents" } }`)) }) diff --git a/jsonapi/marshal_enum_test.go b/jsonapi/marshal_enum_test.go index 3ad3ad4..5484fc7 100644 --- a/jsonapi/marshal_enum_test.go +++ b/jsonapi/marshal_enum_test.go @@ -54,24 +54,29 @@ func (s *PublishStatus) UnmarshalJSON(data []byte) error { type EnumPost struct { ID string `json:"-"` + LID string `json:"-"` Title string `json:"title"` Status PublishStatus `json:"status"` } -func (e EnumPost) GetID() string { - return e.ID +func (e EnumPost) GetID() Identifier { + return Identifier{ID: e.ID, LID: e.LID} } -func (e *EnumPost) SetID(ID string) error { - e.ID = ID +func (e EnumPost) GetName() string { + return "enum-posts" +} +func (e *EnumPost) SetID(ID Identifier) error { + e.ID = ID.ID + e.LID = ID.LID return nil } var _ = Describe("Custom enum types", func() { status := StatusPublished statusValue := "published" - singleJSON := []byte(`{"data":{"id": "1", "type": "enumPosts", "attributes": {"title":"First Post","status":"published"}}}`) + singleJSON := []byte(`{"data":{"id": "1", "type": "enum-posts", "attributes": {"title":"First Post","status":"published"}}}`) firstPost := EnumPost{ID: "1", Title: "First Post", Status: StatusPublished} Context("When marshaling objects including enumes", func() { diff --git a/jsonapi/marshal_same_type_test.go b/jsonapi/marshal_same_type_test.go index c121cf9..1359539 100644 --- a/jsonapi/marshal_same_type_test.go +++ b/jsonapi/marshal_same_type_test.go @@ -7,14 +7,19 @@ import ( type Node struct { ID string `json:"-"` + LID string `json:"-"` Content string `json:"content"` MotherID string `json:"-"` ChildIDs []string `json:"-"` AbandonedChildIDs []string `json:"-"` } -func (n *Node) GetID() string { - return n.ID +func (n Node) GetID() Identifier { + return Identifier{ID: n.ID, LID: n.LID} +} + +func (n Node) GetName() string { + return "nodes" } func (n *Node) GetReferences() []Reference { diff --git a/jsonapi/marshal_test.go b/jsonapi/marshal_test.go index 6a55b1b..f16cb54 100644 --- a/jsonapi/marshal_test.go +++ b/jsonapi/marshal_test.go @@ -24,7 +24,7 @@ var _ = Describe("Marshalling", func() { }) It("marshals single object without relationships", func() { - user := User{ID: 100, Name: "Nino", Password: "babymaus"} + user := User{ID: 100, LID: 0, Name: "Nino", Password: "babymaus"} i, err := Marshal(user) Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ @@ -58,7 +58,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type": "simplePosts", + "type": "simple-posts", "id": "first", "attributes": { "title": "First Post", @@ -98,7 +98,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simplePosts", + "type": "simple-posts", "id": "first", "attributes": { "title": "First Post", @@ -109,7 +109,7 @@ var _ = Describe("Marshalling", func() { } }, { - "type": "simplePosts", + "type": "simple-posts", "id": "second", "attributes": { "title": "Second Post", @@ -138,7 +138,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simplePosts", + "type": "simple-posts", "id": "first", "attributes": { "title": "First Post", @@ -158,7 +158,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simplePosts", + "type": "simple-posts", "id": "first", "attributes": { "title": "First Post", @@ -169,7 +169,7 @@ var _ = Describe("Marshalling", func() { } }, { - "type": "simplePosts", + "type": "simple-posts", "id": "second", "attributes": { "title": "Second Post", @@ -221,6 +221,86 @@ var _ = Describe("Marshalling", func() { }) }) + Context("When marshaling simple objects with local id", func() { + var ( + firstPost, secondPost SimplePost + created time.Time + ) + + BeforeEach(func() { + created, _ = time.Parse(time.RFC3339, "2014-11-10T16:30:48.823Z") + firstPost = SimplePost{ID: "first", Title: "First Post", Text: "Lipsum", Created: created, LID: "same_as_id"} + secondPost = SimplePost{ID: "second", Title: "Second Post", Text: "Getting more advanced!", Created: created, Updated: created, LID: ""} + }) + + It("marshals single object without relationships", func() { + user := User{ID: 100, Name: "Nino", Password: "babymaus", LID: 100} + i, err := Marshal(user) + Expect(err).To(BeNil()) + Expect(i).To(MatchJSON(`{ + "data": { + "type": "users", + "id": "100", + "lid": "100", + "attributes": { + "name": "Nino" + } + } + }`)) + }) + + It("marshals single object", func() { + i, err := Marshal(firstPost) + Expect(err).To(BeNil()) + Expect(i).To(MatchJSON(`{ + "data": { + "type": "simple-posts", + "id": "first", + "lid": "same_as_id", + "attributes": { + "title": "First Post", + "text": "Lipsum", + "created-date": "2014-11-10T16:30:48.823Z", + "updated-date": "0001-01-01T00:00:00Z", + "size": 0 + } + } + }`)) + }) + + It("marshals collections object", func() { + i, err := Marshal([]SimplePost{firstPost, secondPost}) + Expect(err).To(BeNil()) + Expect(i).To(MatchJSON(`{ + "data": [ + { + "type": "simple-posts", + "id": "first", + "lid": "same_as_id", + "attributes": { + "title": "First Post", + "text": "Lipsum", + "size": 0, + "created-date": "2014-11-10T16:30:48.823Z", + "updated-date": "0001-01-01T00:00:00Z" + } + }, + { + "type": "simple-posts", + "id": "second", + "attributes": { + "title": "Second Post", + "text": "Getting more advanced!", + "size": 0, + "created-date": "2014-11-10T16:30:48.823Z", + "updated-date": "2014-11-10T16:30:48.823Z" + } + } + ] + }`)) + }) + }) + Context("When marshaling objects with custom links", func() { It("contains the custom links in the marshaled data", func() { post := CustomLinksPost{} @@ -590,8 +670,8 @@ var _ = Describe("Marshalling", func() { comment1SubComment1 := Comment{ID: 3, Text: "No you are wrong!", SubCommentsEmpty: true} comment1SubComment2 := Comment{ID: 4, Text: "Nah, he's right!", SubCommentsEmpty: true} comment1 := Comment{ID: 1, Text: "First!", SubComments: []Comment{comment1SubComment1, comment1SubComment2}} - comment2 := Comment{ID: 2, Text: "Second!", SubCommentsEmpty: true} - author := User{ID: 1, Name: "Test Author"} + comment2 := Comment{ID: 2, LID: 2, Text: "Second!", SubCommentsEmpty: true} + author := User{ID: 1, LID: 1, Name: "Test Author"} post1 := Post{ID: 1, Title: "Foobar", Comments: []Comment{comment1, comment2}, Author: &author} i, err := MarshalWithURLs(post1, CompleteServerInformation{}) @@ -612,7 +692,8 @@ var _ = Describe("Marshalling", func() { }, "data": { "type": "users", - "id": "1" + "id": "1", + "lid": "1" } }, "comments": { @@ -627,7 +708,8 @@ var _ = Describe("Marshalling", func() { }, { "type": "comments", - "id": "2" + "id": "2", + "lid": "2" } ] } @@ -637,6 +719,7 @@ var _ = Describe("Marshalling", func() { { "type": "users", "id": "1", + "lid": "1", "attributes": { "name": "Test Author" } @@ -669,6 +752,7 @@ var _ = Describe("Marshalling", func() { { "type": "comments", "id": "2", + "lid": "2", "attributes": { "text": "Second!" }, @@ -813,7 +897,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type": "anotherPosts", + "type": "another-posts", "id": "1", "attributes": {}, "relationships": { @@ -940,7 +1024,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(marshalled).To(MatchJSON(`{ "data": { - "type": "zeroPosts", + "type": "zero-posts", "id": "1", "attributes": { "title": "test", @@ -956,7 +1040,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(marshalled).To(MatchJSON(`{ "data": { - "type": "zeroPostPointers", + "type": "zero-post-pointers", "id": "1", "attributes": { "title": "test", @@ -1145,13 +1229,30 @@ var _ = Describe("Marshalling", func() { }) Context("Slice fields", func() { - It("Marshalls the slice field correctly", func() { - marshalled, err := Marshal(Identity{1234, []string{"user_global"}}) + It("Marshalls the slice field correctly without lid", func() { + marshalled, err := Marshal(Identity{1234, 0, []string{"user_global"}}) + Expect(err).To(BeNil()) + Expect(marshalled).To(MatchJSON(`{ + "data": { + "type": "identities", + "id": "1234", + "attributes": { + "scopes": [ + "user_global" + ] + } + } + }`)) + }) + + It("Marshalls the slice field correctly with lid", func() { + marshalled, err := Marshal(Identity{1234, 1, []string{"user_global"}}) Expect(err).To(BeNil()) Expect(marshalled).To(MatchJSON(`{ "data": { "type": "identities", "id": "1234", + "lid": "1", "attributes": { "scopes": [ "user_global" @@ -1214,9 +1315,9 @@ var _ = Describe("Marshalling", func() { links := getStructRelationships(post, nil) Expect(links["author"]).To(Equal(Relationship{ Data: &RelationshipDataContainer{ - DataObject: &RelationshipData{ + DataObject: &Identifier{ ID: "1", - Type: "users", + Name: "users", }, }, })) @@ -1226,10 +1327,11 @@ var _ = Describe("Marshalling", func() { links := getStructRelationships(post, nil) Expect(links["comments"]).To(Equal(Relationship{ Data: &RelationshipDataContainer{ - DataArray: []RelationshipData{ + DataArray: []Identifier{ { ID: "1", - Type: "comments", + Name: "comments", + LID: "", }, }, }, @@ -1240,9 +1342,9 @@ var _ = Describe("Marshalling", func() { links := getStructRelationships(post, CompleteServerInformation{}) Expect(links["author"]).To(Equal(Relationship{ Data: &RelationshipDataContainer{ - DataObject: &RelationshipData{ + DataObject: &Identifier{ ID: "1", - Type: "users", + Name: "users", }, }, Links: Links{ @@ -1256,9 +1358,9 @@ var _ = Describe("Marshalling", func() { links := getStructRelationships(post, BaseURLServerInformation{}) Expect(links["author"]).To(Equal(Relationship{ Data: &RelationshipDataContainer{ - DataObject: &RelationshipData{ + DataObject: &Identifier{ ID: "1", - Type: "users", + Name: "users", }, }, Links: Links{ @@ -1272,9 +1374,9 @@ var _ = Describe("Marshalling", func() { links := getStructRelationships(post, PrefixServerInformation{}) Expect(links["author"]).To(Equal(Relationship{ Data: &RelationshipDataContainer{ - DataObject: &RelationshipData{ + DataObject: &Identifier{ ID: "1", - Type: "users", + Name: "users", }, }, Links: Links{ diff --git a/jsonapi/unmarshal.go b/jsonapi/unmarshal.go index 9394cb6..0d7f30d 100644 --- a/jsonapi/unmarshal.go +++ b/jsonapi/unmarshal.go @@ -10,19 +10,19 @@ import ( // The UnmarshalIdentifier interface must be implemented to set the ID during // unmarshalling. type UnmarshalIdentifier interface { - SetID(string) error + SetID(Identifier) error } // The UnmarshalToOneRelations interface must be implemented to unmarshal // to-one relations. type UnmarshalToOneRelations interface { - SetToOneReferenceID(name, ID string) error + SetToOneReferenceID(name string, ID *Identifier) error } // The UnmarshalToManyRelations interface must be implemented to unmarshal // to-many relations. type UnmarshalToManyRelations interface { - SetToManyReferenceIDs(name string, IDs []string) error + SetToManyReferenceIDs(name string, IDs []Identifier) error } // The UnmarshalResourceMeta interface must be implemented to unmarshal meta fields inside of data containers @@ -126,7 +126,8 @@ func Unmarshal(data []byte, target interface{}) error { if !ok { return errors.New("existing structs must implement interface MarshalIdentifier") } - if record.ID == marshalCasted.GetID() { + identifier := marshalCasted.GetID() + if record.ID == identifier.ID || (record.LID != "" && record.LID == identifier.LID) { targetRecord = targetValue.Index(i).Addr() break } @@ -175,7 +176,7 @@ func setDataIntoTarget(data *Data, target interface{}) error { } } - if err := castedTarget.SetID(data.ID); err != nil { + if err := castedTarget.SetID(Identifier{ID: data.ID, LID: data.LID}); err != nil { return err } @@ -202,7 +203,7 @@ func setRelationshipIDs(relationships map[string]Relationship, target UnmarshalI return fmt.Errorf("struct %s does not implement UnmarshalToOneRelations", reflect.TypeOf(target)) } - err := castedToOne.SetToOneReferenceID(name, "") + err := castedToOne.SetToOneReferenceID(name, nil) if err != nil { return err } @@ -215,7 +216,7 @@ func setRelationshipIDs(relationships map[string]Relationship, target UnmarshalI if !ok { return fmt.Errorf("struct %s does not implement UnmarshalToOneRelations", reflect.TypeOf(target)) } - err := castedToOne.SetToOneReferenceID(name, rel.Data.DataObject.ID) + err := castedToOne.SetToOneReferenceID(name, rel.Data.DataObject) if err != nil { return err } @@ -227,11 +228,7 @@ func setRelationshipIDs(relationships map[string]Relationship, target UnmarshalI if !ok { return fmt.Errorf("struct %s does not implement UnmarshalToManyRelations", reflect.TypeOf(target)) } - IDs := make([]string, len(rel.Data.DataArray)) - for index, relData := range rel.Data.DataArray { - IDs[index] = relData.ID - } - err := castedToMany.SetToManyReferenceIDs(name, IDs) + err := castedToMany.SetToManyReferenceIDs(name, rel.Data.DataArray) if err != nil { return err } diff --git a/jsonapi/unmarshal_test.go b/jsonapi/unmarshal_test.go index ed0f058..f412f6d 100644 --- a/jsonapi/unmarshal_test.go +++ b/jsonapi/unmarshal_test.go @@ -17,11 +17,12 @@ var _ = Describe("Unmarshal", func() { t, _ := time.Parse(time.RFC3339, "2014-11-10T16:30:48.823Z") firstPost := SimplePost{ID: "1", Title: "First Post", Text: "Lipsum", Created: t} secondPost := SimplePost{ID: "2", Title: "Second Post", Text: "Foobar!", Created: t, Updated: t} + secondPostWithLID := SimplePost{ID: "2", LID: "2", Title: "Second Post", Text: "Foobar!", Created: t, Updated: t} singlePostJSON := []byte(`{ "data": { "id": "1", - "type": "simplePosts", + "type": "simple-posts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -34,7 +35,7 @@ var _ = Describe("Unmarshal", func() { "data": [ { "id": "1", - "type": "simplePosts", + "type": "simple-posts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -54,6 +55,31 @@ var _ = Describe("Unmarshal", func() { ] }`) + multiplePostJSONWithLID := []byte(`{ + "data": [ + { + "id": "1", + "type": "simplePosts", + "attributes": { + "title": "First Post", + "text": "Lipsum", + "created-date": "2014-11-10T16:30:48.823Z" + } + }, + { + "id": "2", + "lid": "2", + "type": "simple-posts", + "attributes": { + "title": "Second Post", + "text": "Foobar!", + "created-date": "2014-11-10T16:30:48.823Z", + "updated-date": "2014-11-10T16:30:48.823Z" + } + } + ] + }`) + It("unmarshals single object into a struct", func() { var post SimplePost err := Unmarshal(singlePostJSON, &post) @@ -68,6 +94,13 @@ var _ = Describe("Unmarshal", func() { Expect(posts).To(Equal([]SimplePost{firstPost, secondPost})) }) + It("unmarshals multiple objects into a slice with LID", func() { + var posts []SimplePost + err := Unmarshal(multiplePostJSONWithLID, &posts) + Expect(err).To(BeNil()) + Expect(posts).To(Equal([]SimplePost{firstPost, secondPostWithLID})) + }) + It("unmarshals array attributes into array structs", func() { expected := Image{ ID: "one", @@ -101,7 +134,7 @@ var _ = Describe("Unmarshal", func() { It("errors on existing record that does not implement MarshalIdentifier", func() { type invalid struct{} invalids := []interface{}{invalid{}} - err := Unmarshal(multiplePostJSON, &invalids) + err := Unmarshal(multiplePostJSONWithLID, &invalids) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("existing structs must implement interface MarshalIdentifier")) }) @@ -160,7 +193,7 @@ var _ = Describe("Unmarshal", func() { "text": "blubb", "internal": "1337" }, - "type": "simplePosts" + "type": "simple-posts" } }`), &post) Expect(err).ShouldNot(HaveOccurred()) @@ -174,7 +207,7 @@ var _ = Describe("Unmarshal", func() { "text": "Gopher", "size": "blubb" }, - "type": "simplePosts" + "type": "simple-posts" } }`), &post) Expect(err).To(HaveOccurred()) @@ -192,7 +225,7 @@ var _ = Describe("Unmarshal", func() { "text": "` + firstPost.Text + `!", "created-date": "` + t.Format(time.RFC1123) + `" }, - "type": "simplePosts" + "type": "simple-posts" } }`) var post SimplePost @@ -204,7 +237,7 @@ var _ = Describe("Unmarshal", func() { It("empty attributes is OK", func() { json := []byte(`{ "data": [{ - "type": "simplePosts" + "type": "simple-posts" }] }`) var posts []SimplePost @@ -317,6 +350,64 @@ var _ = Describe("Unmarshal", func() { }) }) + Context("When unmarshaling simple objects wtih local id", func() { + t, _ := time.Parse(time.RFC3339, "2014-11-10T16:30:48.823Z") + firstPost := SimplePost{ID: "1", Title: "First Post", Text: "Lipsum", Created: t, LID: "1"} + secondPost := SimplePost{ID: "2", Title: "Second Post", Text: "Foobar!", Created: t, Updated: t, LID: ""} + + singlePostJSON := []byte(`{ + "data": { + "id": "1", + "lid": "1", + "type": "simple-posts", + "attributes": { + "title": "First Post", + "text": "Lipsum", + "created-date": "2014-11-10T16:30:48.823Z" + } + } + }`) + + multiplePostJSON := []byte(`{ + "data": [ + { + "id": "1", + "lid": "1", + "type": "simple-posts", + "attributes": { + "title": "First Post", + "text": "Lipsum", + "created-date": "2014-11-10T16:30:48.823Z" + } + }, + { + "id": "2", + "type": "simple-posts", + "attributes": { + "title": "Second Post", + "text": "Foobar!", + "created-date": "2014-11-10T16:30:48.823Z", + "updated-date": "2014-11-10T16:30:48.823Z" + } + } + ] + }`) + + It("unmarshals single object into a struct", func() { + var post SimplePost + err := Unmarshal(singlePostJSON, &post) + Expect(err).ToNot(HaveOccurred()) + Expect(post).To(Equal(firstPost)) + }) + + It("unmarshals multiple objects into a slice", func() { + var posts []SimplePost + err := Unmarshal(multiplePostJSON, &posts) + Expect(err).To(BeNil()) + Expect(posts).To(Equal([]SimplePost{firstPost, secondPost})) + }) + }) + Context("when unmarshaling objects with relationships", func() { It("unmarshals to-many relationship IDs", func() { expectedPost := Post{ID: 1, CommentsIDs: []int{1}} @@ -403,7 +494,7 @@ var _ = Describe("Unmarshal", func() { }) It("unmarshals empty relationships", func() { - expectedPost := Post{ID: 3, Title: "Test", AuthorID: sql.NullInt64{Valid: false, Int64: 0}, Author: nil, CommentsIDs: []int{}} + expectedPost := Post{ID: 3, Title: "Test", AuthorID: sql.NullInt64{Valid: false, Int64: 0}, Author: nil, CommentsIDs: nil} postJSON := []byte(`{ "data": { "id": "3", @@ -539,6 +630,89 @@ var _ = Describe("Unmarshal", func() { }) }) + Context("when unmarshaling objects with relationships with local ids", func() { + It("unmarshals to-one and to-many relationship IDs", func() { + expectedPost := Post{ID: 1, LID: 1, AuthorID: sql.NullInt64{Valid: false, Int64: 0}, AuthorLID: sql.NullInt64{Valid: true, Int64: 1}, CommentsIDs: []int{1}, CommentsLIDs: []int{2}} + postJSON := []byte(`{ + "data": { + "id": "1", + "lid": "1", + "type": "posts", + "attributes": {}, + "relationships": { + "author": { + "data": { + "lid": "1", + "type": "users" + } + }, + "comments": { + "data": [ + { + "id": "1", + "type": "links" + }, + { + "lid": "2", + "type": "links" + }] + } + } + } + }`) + var post Post + err := Unmarshal(postJSON, &post) + Expect(err).To(BeNil()) + Expect(expectedPost).To(Equal(post)) + }) + + It("unmarshals to-one and to-many relationship IDs when included", func() { + expectedPost := Post{ID: 1, AuthorID: sql.NullInt64{Valid: false, Int64: 0}, AuthorLID: sql.NullInt64{Valid: true, Int64: 1}, CommentsLIDs: []int{1}} + postJSON := []byte(`{ + "data": { + "id": "1", + "type": "posts", + "attributes": {}, + "relationships": { + "author": { + "data": { + "lid": "1", + "type": "users" + } + }, + "comments": { + "data": [ + { + "lid": "1", + "type": "comments" + }] + } + } + }, + "included": [ + { + "type": "users", + "lid": "1", + "attributes": { + "name": "new user" + } + }, + { + "type": "comments", + "lid": "1", + "attributes": { + "text": "Does this test work?" + } + } + ] + }`) + var post Post + err := Unmarshal(postJSON, &post) + Expect(err).To(BeNil()) + Expect(expectedPost).To(Equal(post)) + }) + }) + It("check if type field matches target struct", func() { postJSON := []byte(`{ "data": { @@ -579,7 +753,7 @@ var _ = Describe("Unmarshal", func() { postJSON := []byte(`{ "data": { "id": "1", - "type": "simplePosts", + "type": "simple-posts", "attributes": { "title": "Nice Title", "text": null @@ -599,7 +773,7 @@ var _ = Describe("Unmarshal", func() { postJSON := []byte(` { "data": { - "type": "simplePosts", + "type": "simple-posts", "attributes": { "title": "Nice Title" } From 45598fa55f6d1fecd79e2a03b2624a8283c32218 Mon Sep 17 00:00:00 2001 From: Ivan Rozhkov Date: Wed, 14 Oct 2020 09:00:11 +0300 Subject: [PATCH 2/4] Add name in Identifier Identifier is a usefull object for which has id, local id and entity name/type. This update should fit the upcoming jsonapi v1.1 release where each object identifier hat this three attributes. --- api.go | 7 +- api_entity_name_test.go | 8 +- api_interfaces_test.go | 46 +++++---- api_test.go | 20 ---- examples/model/model_chocolate.go | 5 - examples/model/model_user.go | 5 - jsonapi/entity_namer.go | 9 -- jsonapi/fixtures_test.go | 108 ++++------------------ jsonapi/integration_test.go | 14 +-- jsonapi/marshal.go | 17 ++-- jsonapi/marshal_composition_test.go | 8 +- jsonapi/marshal_different_to_many_test.go | 6 +- jsonapi/marshal_enum_test.go | 6 +- jsonapi/marshal_same_type_test.go | 4 - jsonapi/marshal_test.go | 30 +++--- jsonapi/unmarshal.go | 2 +- jsonapi/unmarshal_test.go | 24 ++--- 17 files changed, 85 insertions(+), 234 deletions(-) delete mode 100644 jsonapi/entity_namer.go diff --git a/api.go b/api.go index 3a2724e..5d2e381 100644 --- a/api.go +++ b/api.go @@ -217,10 +217,9 @@ func (api *API) addResource(prototype jsonapi.MarshalIdentifier, source interfac name = resourceType.Elem().Name() } - // check if EntityNamer interface is implemented and use that as name - entityName, ok := prototype.(jsonapi.EntityNamer) - if ok { - name = entityName.GetName() + identifier := prototype.GetID() + if identifier.Name != "" { + name = identifier.Name } else { name = jsonapi.Jsonify(jsonapi.Pluralize(name)) } diff --git a/api_entity_name_test.go b/api_entity_name_test.go index 3f86247..266a9c9 100644 --- a/api_entity_name_test.go +++ b/api_entity_name_test.go @@ -17,7 +17,7 @@ type BaguetteTaste struct { } func (s BaguetteTaste) GetID() jsonapi.Identifier { - return jsonapi.Identifier{ID: s.ID, LID: s.LID} + return jsonapi.Identifier{ID: s.ID, LID: s.LID, Name: "baguette-tastes"} } func (s *BaguetteTaste) SetID(ID jsonapi.Identifier) error { @@ -26,10 +26,6 @@ func (s *BaguetteTaste) SetID(ID jsonapi.Identifier) error { return nil } -func (s BaguetteTaste) GetName() string { - return "baguette-tastes" -} - type BaguetteResource struct{} func (s BaguetteResource) FindOne(ID string, req Request) (Responder, error) { @@ -72,7 +68,7 @@ func (s BaguetteResource) Update(obj interface{}, req Request) (Responder, error }, nil } -var _ = Describe("Test route renaming with EntityNamer interface", func() { +var _ = Describe("Test route renaming with Identifier name", func() { var ( api *API rec *httptest.ResponseRecorder diff --git a/api_interfaces_test.go b/api_interfaces_test.go index 5347325..3494268 100644 --- a/api_interfaces_test.go +++ b/api_interfaces_test.go @@ -23,10 +23,6 @@ func (s SomeData) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: s.ID, LID: s.LID} } -func (s SomeData) GetName() string { - return "some-datas" -} - func (s *SomeData) SetID(ID jsonapi.Identifier) error { s.ID = ID.ID s.LID = ID.LID @@ -126,22 +122,22 @@ var _ = Describe("Test interface api type casting", func() { }) It("FindAll returns 404 for simple CRUD", func() { - req, err := http.NewRequest("GET", "/v1/some-datas", nil) + req, err := http.NewRequest("GET", "/v1/someDatas", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusNotFound)) }) It("Works for a normal FindOne", func() { - req, err := http.NewRequest("GET", "/v1/some-datas/12345", nil) + req, err := http.NewRequest("GET", "/v1/someDatas/12345", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusOK)) }) It("Post works with lowercase renaming", func() { - reqBody := strings.NewReader(`{"data": {"attributes":{"customerId": "2" }, "type": "some-datas"}}`) - req, err := http.NewRequest("POST", "/v1/some-datas", reqBody) + reqBody := strings.NewReader(`{"data": {"attributes":{"customerId": "2" }, "type": "someDatas"}}`) + req, err := http.NewRequest("POST", "/v1/someDatas", reqBody) Expect(err).To(BeNil()) api.Handler().ServeHTTP(rec, req) Expect(rec.Code).To(Equal(http.StatusCreated)) @@ -167,7 +163,7 @@ var _ = Describe("Test return code behavior", func() { post := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) + req, err := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -199,7 +195,7 @@ var _ = Describe("Test return code behavior", func() { var err HTTPError json.Unmarshal(rec.Body.Bytes(), &err) Expect(err.Errors[0]).To(Equal(Error{ - Title: "invalid status code 418 from resource some-datas for method Create", + Title: "invalid status code 418 from resource someDatas for method Create", Status: strconv.Itoa(http.StatusInternalServerError)})) }) @@ -224,7 +220,7 @@ var _ = Describe("Test return code behavior", func() { patch := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) + req, err := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -256,7 +252,7 @@ var _ = Describe("Test return code behavior", func() { var err HTTPError json.Unmarshal(rec.Body.Bytes(), &err) Expect(err.Errors[0]).To(Equal(Error{ - Title: "invalid status code 418 from resource some-datas for method Update", + Title: "invalid status code 418 from resource someDatas for method Update", Status: strconv.Itoa(http.StatusInternalServerError)})) }) @@ -274,7 +270,7 @@ var _ = Describe("Test return code behavior", func() { Context("Delete", func() { delete := func(ID string) { - req, err := http.NewRequest("DELETE", "/v1/some-datas/"+ID, nil) + req, err := http.NewRequest("DELETE", "/v1/someDatas/"+ID, nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -324,7 +320,7 @@ var _ = Describe("Test partial CRUD implementation : Creator", func() { post := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) + req, err := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -342,17 +338,17 @@ var _ = Describe("Test partial CRUD implementation : Creator", func() { // Test PATCH m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPatch, errPatch := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) + reqPatch, errPatch := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) Expect(errPatch).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPatch) Expect(rec.Code).To(Equal(http.StatusNotFound)) // Test DELETE - reqDelete, errDelete := http.NewRequest("DELETE", "/v1/some-datas/12345", nil) + reqDelete, errDelete := http.NewRequest("DELETE", "/v1/someDatas/12345", nil) Expect(errDelete).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqDelete) Expect(rec.Code).To(Equal(http.StatusNotFound)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusNotFound)) @@ -379,7 +375,7 @@ var _ = Describe("Test partial CRUD implementation : Updater", func() { patch := func(payload SomeData) { m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - req, err := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) + req, err := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -397,17 +393,17 @@ var _ = Describe("Test partial CRUD implementation : Updater", func() { // Test POST m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPost, errPost := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) + reqPost, errPost := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) Expect(errPost).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPost) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test DELETE - reqDelete, errDelete := http.NewRequest("DELETE", "/v1/some-datas/12345", nil) + reqDelete, errDelete := http.NewRequest("DELETE", "/v1/someDatas/12345", nil) Expect(errDelete).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqDelete) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) @@ -432,7 +428,7 @@ var _ = Describe("Test partial CRUD implementation : Deleter", func() { Context("Delete", func() { delete := func() { - req, err := http.NewRequest("DELETE", "/v1/some-datas/1234", nil) + req, err := http.NewRequest("DELETE", "/v1/someDatas/1234", nil) Expect(err).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, req) } @@ -446,17 +442,17 @@ var _ = Describe("Test partial CRUD implementation : Deleter", func() { // Test POST m, err := jsonapi.Marshal(payload) Expect(err).ToNot(HaveOccurred()) - reqPost, errPost := http.NewRequest("POST", "/v1/some-datas", strings.NewReader(string(m))) + reqPost, errPost := http.NewRequest("POST", "/v1/someDatas", strings.NewReader(string(m))) Expect(errPost).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPost) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test PATCH - reqPatch, errPatch := http.NewRequest("PATCH", "/v1/some-datas/12345", strings.NewReader(string(m))) + reqPatch, errPatch := http.NewRequest("PATCH", "/v1/someDatas/12345", strings.NewReader(string(m))) Expect(errPatch).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqPatch) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) // Test GET item - reqRead, errRead := http.NewRequest("GET", "/v1/some-datas/12345", nil) + reqRead, errRead := http.NewRequest("GET", "/v1/someDatas/12345", nil) Expect(errRead).ToNot(HaveOccurred()) api.Handler().ServeHTTP(rec, reqRead) Expect(rec.Code).To(Equal(http.StatusMethodNotAllowed)) diff --git a/api_test.go b/api_test.go index 7a5de94..96a5ef0 100644 --- a/api_test.go +++ b/api_test.go @@ -41,10 +41,6 @@ func (i invalid) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: "invalid", LID: "invalid"} } -func (i invalid) GetName() string { - return "invalid" -} - type Post struct { ID string `json:"-"` LID string `json:"-"` @@ -59,10 +55,6 @@ func (p Post) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: p.ID, LID: p.LID} } -func (p Post) GetName() string { - return "posts" -} - func (p *Post) SetID(ID jsonapi.Identifier) error { p.ID = ID.ID p.LID = ID.LID @@ -205,10 +197,6 @@ func (c Comment) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: c.ID, LID: c.LID} } -func (c Comment) GetName() string { - return "comments" -} - type Banana struct { ID string `jnson:"-"` LID string `jnson:"-"` @@ -219,10 +207,6 @@ func (b Banana) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: b.ID, LID: b.LID} } -func (b Banana) GetName() string { - return "bananas" -} - type User struct { ID string `json:"-"` LID string `json:"-"` @@ -234,10 +218,6 @@ func (u User) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: u.ID, LID: u.LID} } -func (u User) GetName() string { - return "users" -} - type fixtureSource struct { posts map[string]*Post pointers bool diff --git a/examples/model/model_chocolate.go b/examples/model/model_chocolate.go index 79c24a2..4532b34 100644 --- a/examples/model/model_chocolate.go +++ b/examples/model/model_chocolate.go @@ -15,11 +15,6 @@ func (c Chocolate) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: c.ID, LID: c.LID} } -// GetName to satisfy jsonapi.MarshalIdentifier interface -func (c Chocolate) GetName() string { - return "chocolates" -} - // SetID to satisfy jsonapi.UnmarshalIdentifier interface func (c *Chocolate) SetID(ID jsonapi.Identifier) error { c.ID = ID.ID diff --git a/examples/model/model_user.go b/examples/model/model_user.go index c788bd5..bd393ee 100644 --- a/examples/model/model_user.go +++ b/examples/model/model_user.go @@ -23,11 +23,6 @@ func (u User) GetID() jsonapi.Identifier { return jsonapi.Identifier{ID: u.ID, LID: u.LID} } -// GetName to satisfy jsonapi.MarshalIdentifier interface -func (u User) GetName() string { - return "users" -} - // SetID to satisfy jsonapi.UnmarshalIdentifier interface func (u *User) SetID(ID jsonapi.Identifier) error { u.ID = ID.ID diff --git a/jsonapi/entity_namer.go b/jsonapi/entity_namer.go deleted file mode 100644 index ca56120..0000000 --- a/jsonapi/entity_namer.go +++ /dev/null @@ -1,9 +0,0 @@ -package jsonapi - -// The EntityNamer interface can be optionally implemented to directly return the -// name of resource used for the "type" field. -// -// Note: By default the name is guessed from the struct name. -type EntityNamer interface { - GetName() string -} diff --git a/jsonapi/fixtures_test.go b/jsonapi/fixtures_test.go index 23c464c..64d0eaa 100644 --- a/jsonapi/fixtures_test.go +++ b/jsonapi/fixtures_test.go @@ -19,10 +19,6 @@ func (m Magic) GetID() Identifier { return Identifier{ID: m.ID.String()} } -func (m Magic) GetName() string { - return "magics" -} - type MagicID string func (m MagicID) String() string { @@ -45,10 +41,6 @@ func (c Comment) GetID() Identifier { return id } -func (c Comment) GetName() string { - return "comments" -} - func (c *Comment) SetID(ID Identifier) error { if ID.ID != "" { id, err := strconv.Atoi(ID.ID) @@ -115,10 +107,6 @@ func (u User) GetID() Identifier { return id } -func (u User) GetName() string { - return "users" -} - func (u *User) SetID(ID Identifier) error { id, err := strconv.Atoi(ID.ID) if err != nil { @@ -150,10 +138,6 @@ func (s SimplePost) GetID() Identifier { return Identifier{ID: s.ID, LID: s.LID} } -func (s SimplePost) GetName() string { - return "simple-posts" -} - func (s *SimplePost) SetID(ID Identifier) error { s.ID = ID.ID s.LID = ID.LID @@ -200,10 +184,6 @@ func (c Post) GetID() Identifier { return id } -func (c Post) GetName() string { - return "posts" -} - func (c *Post) SetID(ID Identifier) error { if ID.ID != "" { id, err := strconv.Atoi(ID.ID) @@ -387,10 +367,6 @@ func (p AnotherPost) GetID() Identifier { return id } -func (p AnotherPost) GetName() string { - return "another-posts" -} - func (p AnotherPost) GetReferences() []Reference { return []Reference{ { @@ -421,10 +397,6 @@ func (z ZeroPost) GetID() Identifier { return Identifier{ID: z.ID, LID: z.LID} } -func (z ZeroPost) GetName() string { - return "zero-posts" -} - type ZeroPostPointer struct { ID string `json:"-"` LID string `json:"-"` @@ -436,10 +408,6 @@ func (z ZeroPostPointer) GetID() Identifier { return Identifier{ID: z.ID, LID: z.LID} } -func (z ZeroPostPointer) GetName() string { - return "zero-post-pointers" -} - type Question struct { ID string `json:"-"` Text string `json:"text"` @@ -451,10 +419,6 @@ func (q Question) GetID() Identifier { return Identifier{ID: q.ID, LID: ""} } -func (q Question) GetName() string { - return "questions" -} - func (q Question) GetReferences() []Reference { return []Reference{ { @@ -498,10 +462,6 @@ func (i Identity) GetID() Identifier { return id } -func (i Identity) GetName() string { - return "identities" -} - func (i *Identity) SetID(ID Identifier) error { if ID.ID != "" { id, err := strconv.Atoi(ID.ID) @@ -530,10 +490,6 @@ func (u Unicorn) GetID() Identifier { return Identifier{ID: "magicalUnicorn", LID: ""} } -func (u Unicorn) GetName() string { - return "unicorns" -} - type NumberPost struct { ID string `json:"-"` LID string `json:"-"` @@ -542,6 +498,10 @@ type NumberPost struct { UnsignedNumber uint64 } +func (n NumberPost) GetID() Identifier{ + return Identifier{ID: n.ID, LID: n.LID} +} + func (n *NumberPost) SetID(ID Identifier) error { n.ID = ID.ID n.LID = ID.LID @@ -559,7 +519,7 @@ type SQLNullPost struct { } func (s SQLNullPost) GetID() Identifier { - return Identifier{ID: s.ID, LID: s.LID} + return Identifier{ID: s.ID, LID: s.LID, Name: "sqlNullPosts"} } func (s *SQLNullPost) SetID(ID Identifier) error { @@ -578,26 +538,22 @@ type RenamedPostWithEmbedding struct { Ignored string `json:"-"` } +func (p RenamedPostWithEmbedding) GetID() Identifier { + return Identifier{ID: p.ID, LID: p.LID} +} + func (p *RenamedPostWithEmbedding) SetID(ID Identifier) error { p.ID = ID.ID p.LID = ID.LID return nil } -func (s SQLNullPost) GetName() string { - return "sqlNullPosts" -} - type RenamedComment struct { Data string } func (r RenamedComment) GetID() Identifier { - return Identifier{ID: "666", LID: ""} -} - -func (r RenamedComment) GetName() string { - return "renamed-comments" + return Identifier{ID: "666", LID: "", Name: "renamed-comments"} } type CompleteServerInformation struct{} @@ -636,17 +592,13 @@ func (i PrefixServerInformation) GetPrefix() string { type CustomLinksPost struct{} func (n CustomLinksPost) GetID() Identifier { - return Identifier{ID: "someID", LID: ""} + return Identifier{ID: "someID", LID: "", Name: "posts"} } func (n *CustomLinksPost) SetID(ID Identifier) error { return nil } -func (n CustomLinksPost) GetName() string { - return "posts" -} - func (n CustomLinksPost) GetCustomLinks(base string) Links { return Links{ "nothingInHere": Link{}, @@ -663,17 +615,13 @@ func (n CustomLinksPost) GetCustomLinks(base string) Links { type CustomResourceMetaPost struct{} func (n CustomResourceMetaPost) GetID() Identifier { - return Identifier{ID: "someID", LID: ""} + return Identifier{ID: "someID", LID: "", Name: "posts"} } func (n *CustomResourceMetaPost) SetID(ID Identifier) error { return nil } -func (n CustomResourceMetaPost) GetName() string { - return "posts" -} - func (n CustomResourceMetaPost) Meta() Meta { return Meta{"access_count": 15} } @@ -681,17 +629,13 @@ func (n CustomResourceMetaPost) Meta() Meta { type CustomMetaPost struct{} func (n CustomMetaPost) GetID() Identifier { - return Identifier{ID: "someID", LID: ""} + return Identifier{ID: "someID", LID: "", Name: "posts"} } func (n *CustomMetaPost) SetID(ID Identifier) error { return nil } -func (n CustomMetaPost) GetName() string { - return "posts" -} - func (n CustomMetaPost) GetReferences() []Reference { return []Reference{ { @@ -719,31 +663,23 @@ func (n CustomMetaPost) GetCustomMeta(linkURL string) map[string]Meta { type NoRelationshipPosts struct{} func (n NoRelationshipPosts) GetID() Identifier { - return Identifier{ID: "someID", LID: ""} + return Identifier{ID: "someID", LID: "", Name: "posts"} } func (n *NoRelationshipPosts) SetID(ID Identifier) error { return nil } -func (n NoRelationshipPosts) GetName() string { - return "posts" -} - type ErrorRelationshipPosts struct{} func (e ErrorRelationshipPosts) GetID() Identifier { - return Identifier{ID: "errorID", LID: ""} + return Identifier{ID: "errorID", LID: "", Name: "posts"} } func (e *ErrorRelationshipPosts) SetID(ID Identifier) error { return nil } -func (e ErrorRelationshipPosts) GetName() string { - return "posts" -} - func (e ErrorRelationshipPosts) SetToOneReferenceID(name string, ID *Identifier) error { return errors.New("this never works") } @@ -762,10 +698,6 @@ func (i Image) GetID() Identifier { return Identifier{ID: i.ID, LID: i.LID} } -func (i Image) GetName() string { - return "images" -} - func (i *Image) SetID(ID Identifier) error { i.ID = ID.ID i.LID = ID.LID @@ -788,10 +720,6 @@ func (a Article) GetID() Identifier { return Identifier{ID: "id", LID: ""} } -func (a Article) GetName() string { - return "articles" -} - func (a Article) GetReferences() []Reference { return []Reference{{Type: a.Type, Name: a.Name, Relationship: a.Relationship}} } @@ -812,11 +740,7 @@ type DeepDedendencies struct { } func (d DeepDedendencies) GetID() Identifier { - return Identifier{ID: d.ID, LID: ""} -} - -func (DeepDedendencies) GetName() string { - return "deep" + return Identifier{ID: d.ID, LID: "", Name: "deep"} } func (d DeepDedendencies) GetReferences() []Reference { diff --git a/jsonapi/integration_test.go b/jsonapi/integration_test.go index c8f4e23..d84475a 100644 --- a/jsonapi/integration_test.go +++ b/jsonapi/integration_test.go @@ -21,10 +21,6 @@ func (b Book) GetID() Identifier { return Identifier{ID: b.ID, LID: b.LID} } -func (b Book) GetName() string { - return "books" -} - func (b *Book) SetID(ID Identifier) error { b.ID = ID.ID b.LID = ID.LID @@ -117,10 +113,6 @@ func (s StupidUser) GetID() Identifier { return Identifier{ID: s.ID, LID: ""} } -func (s StupidUser) GetName() string { - return "stupid-users" -} - type Page struct { ID string `json:"-"` Content string `json:"content"` @@ -130,10 +122,6 @@ func (p Page) GetID() Identifier { return Identifier{ID: p.ID, LID: ""} } -func (p Page) GetName() string { - return "pages" -} - var _ = Describe("Test for the public api of this package", func() { author := StupidUser{ ID: "A Magical UserID", @@ -188,7 +176,7 @@ var _ = Describe("Test for the public api of this package", func() { "attributes": { "name" : "Terry Pratchett" }, - "type" : "stupid-users" + "type" : "stupidUsers" }, { "attributes": { diff --git a/jsonapi/marshal.go b/jsonapi/marshal.go index c11f04c..a8fe65b 100644 --- a/jsonapi/marshal.go +++ b/jsonapi/marshal.go @@ -26,7 +26,6 @@ const ( // Note: The implementation of this interface is mandatory. type MarshalIdentifier interface { GetID() Identifier - EntityNamer } type Identifier struct { @@ -220,12 +219,13 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) for _, referencedStruct := range input { structType := getStructType(referencedStruct) + id := referencedStruct.GetID() if alreadyIncluded[structType] == nil { alreadyIncluded[structType] = make(map[string]bool) } - if !alreadyIncluded[structType][referencedStruct.GetID().ID] { + if !alreadyIncluded[structType][id.ID] { var data Data err := marshalData(referencedStruct, &data, information) if err != nil { @@ -233,7 +233,7 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) } includedElements = append(includedElements, data) - alreadyIncluded[structType][referencedStruct.GetID().ID] = true + alreadyIncluded[structType][id.ID] = true } } @@ -405,13 +405,12 @@ func getStructRelationships(relationer MarshalLinkedRelations, information Serve func getLinkBaseURL(element MarshalIdentifier, information ServerInformation) string { prefix := strings.Trim(information.GetBaseURL(), "/") namespace := strings.Trim(information.GetPrefix(), "/") - structType := getStructType(element) if namespace != "" { prefix += "/" + namespace } - return fmt.Sprintf("%s/%s/%s", prefix, structType, element.GetID().ID) + return fmt.Sprintf("%s/%s/%s", prefix, getStructType(element), element.GetID().ID) } func getLinksForServerInformation(relationer MarshalLinkedRelations, name string, information ServerInformation) Links { @@ -458,9 +457,9 @@ func marshalStruct(data MarshalIdentifier, information ServerInformation) (*Docu } func getStructType(data interface{}) string { - entityName, ok := data.(EntityNamer) - if ok { - return entityName.GetName() + identifier, ok := data.(MarshalIdentifier) + if ok && identifier.GetID().Name != "" { + return identifier.GetID().Name } reflectType := reflect.TypeOf(data) @@ -469,4 +468,4 @@ func getStructType(data interface{}) string { } return Pluralize(Jsonify(reflectType.Name())) -} +} \ No newline at end of file diff --git a/jsonapi/marshal_composition_test.go b/jsonapi/marshal_composition_test.go index ca99ea4..0ac72b1 100644 --- a/jsonapi/marshal_composition_test.go +++ b/jsonapi/marshal_composition_test.go @@ -12,8 +12,8 @@ type TaggedPost struct { Tag string `json:"tag"` } -func (p TaggedPost) GetName() string { - return "tagged-posts" +func (t TaggedPost) GetID() Identifier { + return Identifier{ID: t.ID, LID: t.LID} } var _ = Describe("Embedded struct types", func() { @@ -29,7 +29,7 @@ var _ = Describe("Embedded struct types", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type":"tagged-posts", + "type":"taggedPosts", "id":"first", "attributes": { "title":"First Post", @@ -47,7 +47,7 @@ var _ = Describe("Embedded struct types", func() { Context("When unmarshaling objects with struct composition", func() { postJSON := `{ "data": { - "type": "tagged-posts", + "type": "taggedPosts", "id": "first", "attributes": { "title": "First Post", diff --git a/jsonapi/marshal_different_to_many_test.go b/jsonapi/marshal_different_to_many_test.go index 87cdc91..4f68351 100644 --- a/jsonapi/marshal_different_to_many_test.go +++ b/jsonapi/marshal_different_to_many_test.go @@ -15,10 +15,6 @@ func (m ManyParent) GetID() Identifier { return Identifier{ID: m.ID, LID: m.LID} } -func (m ManyParent) GetName() string { - return "many-parents" -} - func (m ManyParent) GetReferences() []Reference { return []Reference{ { @@ -76,7 +72,7 @@ var _ = Describe("Marshalling toMany relations with the same name and different ] } }, - "type": "many-parents" + "type": "manyParents" } }`)) }) diff --git a/jsonapi/marshal_enum_test.go b/jsonapi/marshal_enum_test.go index 5484fc7..ed7543b 100644 --- a/jsonapi/marshal_enum_test.go +++ b/jsonapi/marshal_enum_test.go @@ -63,10 +63,6 @@ func (e EnumPost) GetID() Identifier { return Identifier{ID: e.ID, LID: e.LID} } -func (e EnumPost) GetName() string { - return "enum-posts" -} - func (e *EnumPost) SetID(ID Identifier) error { e.ID = ID.ID e.LID = ID.LID @@ -76,7 +72,7 @@ func (e *EnumPost) SetID(ID Identifier) error { var _ = Describe("Custom enum types", func() { status := StatusPublished statusValue := "published" - singleJSON := []byte(`{"data":{"id": "1", "type": "enum-posts", "attributes": {"title":"First Post","status":"published"}}}`) + singleJSON := []byte(`{"data":{"id": "1", "type": "enumPosts", "attributes": {"title":"First Post","status":"published"}}}`) firstPost := EnumPost{ID: "1", Title: "First Post", Status: StatusPublished} Context("When marshaling objects including enumes", func() { diff --git a/jsonapi/marshal_same_type_test.go b/jsonapi/marshal_same_type_test.go index 1359539..c912d1b 100644 --- a/jsonapi/marshal_same_type_test.go +++ b/jsonapi/marshal_same_type_test.go @@ -18,10 +18,6 @@ func (n Node) GetID() Identifier { return Identifier{ID: n.ID, LID: n.LID} } -func (n Node) GetName() string { - return "nodes" -} - func (n *Node) GetReferences() []Reference { return []Reference{ { diff --git a/jsonapi/marshal_test.go b/jsonapi/marshal_test.go index f16cb54..6945aa6 100644 --- a/jsonapi/marshal_test.go +++ b/jsonapi/marshal_test.go @@ -58,7 +58,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "attributes": { "title": "First Post", @@ -98,7 +98,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "attributes": { "title": "First Post", @@ -109,7 +109,7 @@ var _ = Describe("Marshalling", func() { } }, { - "type": "simple-posts", + "type": "simplePosts", "id": "second", "attributes": { "title": "Second Post", @@ -138,7 +138,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "attributes": { "title": "First Post", @@ -158,7 +158,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "attributes": { "title": "First Post", @@ -169,7 +169,7 @@ var _ = Describe("Marshalling", func() { } }, { - "type": "simple-posts", + "type": "simplePosts", "id": "second", "attributes": { "title": "Second Post", @@ -254,7 +254,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "lid": "same_as_id", "attributes": { @@ -274,7 +274,7 @@ var _ = Describe("Marshalling", func() { Expect(i).To(MatchJSON(`{ "data": [ { - "type": "simple-posts", + "type": "simplePosts", "id": "first", "lid": "same_as_id", "attributes": { @@ -286,7 +286,7 @@ var _ = Describe("Marshalling", func() { } }, { - "type": "simple-posts", + "type": "simplePosts", "id": "second", "attributes": { "title": "Second Post", @@ -897,7 +897,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(i).To(MatchJSON(`{ "data": { - "type": "another-posts", + "type": "anotherPosts", "id": "1", "attributes": {}, "relationships": { @@ -1024,7 +1024,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(marshalled).To(MatchJSON(`{ "data": { - "type": "zero-posts", + "type": "zeroPosts", "id": "1", "attributes": { "title": "test", @@ -1040,7 +1040,7 @@ var _ = Describe("Marshalling", func() { Expect(err).To(BeNil()) Expect(marshalled).To(MatchJSON(`{ "data": { - "type": "zero-post-pointers", + "type": "zeroPostPointers", "id": "1", "attributes": { "title": "test", @@ -1282,17 +1282,17 @@ var _ = Describe("Marshalling", func() { Context("Test getStructTypes method", func() { comment := Comment{ID: 100, Text: "some text"} - It("should work with normal value", func() { + It("should work with normal value with name in id", func() { result := getStructType(comment) Expect(result).To(Equal("comments")) }) - It("should work with pointer to value", func() { + It("should work with pointer to value with name in id", func() { result := getStructType(&comment) Expect(result).To(Equal("comments")) }) - It("checks for EntityNamer interface", func() { + It("checks for MarshalIdentifier interface", func() { result := getStructType(RenamedComment{"something"}) Expect(result).To(Equal("renamed-comments")) }) diff --git a/jsonapi/unmarshal.go b/jsonapi/unmarshal.go index 0d7f30d..930d5cf 100644 --- a/jsonapi/unmarshal.go +++ b/jsonapi/unmarshal.go @@ -245,4 +245,4 @@ func checkType(incomingType string, target UnmarshalIdentifier) error { } return nil -} +} \ No newline at end of file diff --git a/jsonapi/unmarshal_test.go b/jsonapi/unmarshal_test.go index f412f6d..2e73b5b 100644 --- a/jsonapi/unmarshal_test.go +++ b/jsonapi/unmarshal_test.go @@ -22,7 +22,7 @@ var _ = Describe("Unmarshal", func() { singlePostJSON := []byte(`{ "data": { "id": "1", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -35,7 +35,7 @@ var _ = Describe("Unmarshal", func() { "data": [ { "id": "1", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -69,7 +69,7 @@ var _ = Describe("Unmarshal", func() { { "id": "2", "lid": "2", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "Second Post", "text": "Foobar!", @@ -193,7 +193,7 @@ var _ = Describe("Unmarshal", func() { "text": "blubb", "internal": "1337" }, - "type": "simple-posts" + "type": "simplePosts" } }`), &post) Expect(err).ShouldNot(HaveOccurred()) @@ -207,7 +207,7 @@ var _ = Describe("Unmarshal", func() { "text": "Gopher", "size": "blubb" }, - "type": "simple-posts" + "type": "simplePosts" } }`), &post) Expect(err).To(HaveOccurred()) @@ -225,7 +225,7 @@ var _ = Describe("Unmarshal", func() { "text": "` + firstPost.Text + `!", "created-date": "` + t.Format(time.RFC1123) + `" }, - "type": "simple-posts" + "type": "simplePosts" } }`) var post SimplePost @@ -237,7 +237,7 @@ var _ = Describe("Unmarshal", func() { It("empty attributes is OK", func() { json := []byte(`{ "data": [{ - "type": "simple-posts" + "type": "simplePosts" }] }`) var posts []SimplePost @@ -359,7 +359,7 @@ var _ = Describe("Unmarshal", func() { "data": { "id": "1", "lid": "1", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -373,7 +373,7 @@ var _ = Describe("Unmarshal", func() { { "id": "1", "lid": "1", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "First Post", "text": "Lipsum", @@ -382,7 +382,7 @@ var _ = Describe("Unmarshal", func() { }, { "id": "2", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "Second Post", "text": "Foobar!", @@ -753,7 +753,7 @@ var _ = Describe("Unmarshal", func() { postJSON := []byte(`{ "data": { "id": "1", - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "Nice Title", "text": null @@ -773,7 +773,7 @@ var _ = Describe("Unmarshal", func() { postJSON := []byte(` { "data": { - "type": "simple-posts", + "type": "simplePosts", "attributes": { "title": "Nice Title" } From 24597bbe493cc0c45293c3f8e74c333b1aa1d1cf Mon Sep 17 00:00:00 2001 From: Artem Lobanov Date: Thu, 22 Oct 2020 20:26:26 +0300 Subject: [PATCH 3/4] supports includes with the same ID and different LIDs --- jsonapi/marshal.go | 10 +++---- jsonapi/marshal_test.go | 59 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/jsonapi/marshal.go b/jsonapi/marshal.go index a8fe65b..077ad1e 100644 --- a/jsonapi/marshal.go +++ b/jsonapi/marshal.go @@ -214,7 +214,7 @@ func marshalSlice(data interface{}, information ServerInformation) (*Document, e } func filterDuplicates(input []MarshalIdentifier, information ServerInformation) ([]Data, error) { - alreadyIncluded := map[string]map[string]bool{} + alreadyIncluded := map[string]map[Identifier]bool{} includedElements := []Data{} for _, referencedStruct := range input { @@ -222,10 +222,10 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) id := referencedStruct.GetID() if alreadyIncluded[structType] == nil { - alreadyIncluded[structType] = make(map[string]bool) + alreadyIncluded[structType] = make(map[Identifier]bool) } - if !alreadyIncluded[structType][id.ID] { + if !alreadyIncluded[structType][id] { var data Data err := marshalData(referencedStruct, &data, information) if err != nil { @@ -233,7 +233,7 @@ func filterDuplicates(input []MarshalIdentifier, information ServerInformation) } includedElements = append(includedElements, data) - alreadyIncluded[structType][id.ID] = true + alreadyIncluded[structType][id] = true } } @@ -468,4 +468,4 @@ func getStructType(data interface{}) string { } return Pluralize(Jsonify(reflectType.Name())) -} \ No newline at end of file +} diff --git a/jsonapi/marshal_test.go b/jsonapi/marshal_test.go index 6945aa6..e73bfb8 100644 --- a/jsonapi/marshal_test.go +++ b/jsonapi/marshal_test.go @@ -1093,6 +1093,65 @@ var _ = Describe("Marshalling", func() { }`)) }) + It("Correctly marshalls the same dependencies with different local id", func() { + comment := Comment{ID: 1, Text: "comment", SubComments: []Comment{{LID: 1, Text: "sub comment one"}, {LID: 2, Text: "sub comment two"}}} + marshalled, err := Marshal(comment) + Expect(err).To(BeNil()) + Expect(marshalled).To(MatchJSON(`{ + "data": { + "type": "comments", + "id": "1", + "attributes": { + "text": "comment" + }, + "relationships": { + "comments": { + "data": [ + { + "id": "0", + "lid": "1", + "type": "comments" + }, + { + "id": "0", + "lid": "2", + "type": "comments" + } + ] + } + } + }, + "included": [ + { + "type": "comments", + "id": "0", + "lid": "1", + "attributes": { + "text": "sub comment one" + }, + "relationships": { + "comments": { + "data": [] + } + } + }, + { + "type": "comments", + "id": "0", + "lid": "2", + "attributes": { + "text": "sub comment two" + }, + "relationships": { + "comments": { + "data": [] + } + } + } + ] + }`)) + }) + It("Does not marshall same dependencies multiple times for slice", func() { marshalled, err := Marshal([]Question{question3, question2}) Expect(err).To(BeNil()) From 1d9a0167a7eebe1ab07b287069a2cb1951f5e53f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 17:29:59 +0000 Subject: [PATCH 4/4] Update dependecies ] --- .github/workflows/go.yml | 5 ++++- api_interfaces.go | 6 +++--- api_public.go | 2 +- examples/crud_example.go | 13 ++++++++++++ examples/resolver/resolver.go | 10 ++++----- examples/resource/resource_user.go | 2 +- go.mod | 33 +++++++++++++++++++++++++++--- go.sum | 21 +++++++++++-------- jsonapi/fixtures_test.go | 2 +- jsonapi/unmarshal.go | 2 +- routing/gingonic.go | 2 +- routing/gorillamux.go | 2 +- 12 files changed, 73 insertions(+), 27 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b1da35d..dc1b437 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.17', '1.15', '1.14', '1.13' ] + go: [ '1.18', '1.19', '1.20' ] name: Go ${{ matrix.go }} tests steps: - uses: actions/checkout@v2 @@ -24,9 +24,12 @@ jobs: run: | go get -t -d -v ./... go get github.com/onsi/ginkgo/ginkgo + go install github.com/onsi/ginkgo/ginkgo go get -u golang.org/x/lint/golint go get -u github.com/modocache/gover + go install github.com/modocache/gover go get -u github.com/mattn/goveralls + go install github.com/mattn/goveralls - name: Run tests run: | ginkgo -r -cover --randomizeAllSpecs --randomizeSuites --failOnPending --trace --race --progress diff --git a/api_interfaces.go b/api_interfaces.go index 758addb..1089093 100644 --- a/api_interfaces.go +++ b/api_interfaces.go @@ -84,9 +84,9 @@ type ObjectInitializer interface { InitializeObject(interface{}) } -//URLResolver allows you to implement a static -//way to return a baseURL for all incoming -//requests for one api2go instance. +// URLResolver allows you to implement a static +// way to return a baseURL for all incoming +// requests for one api2go instance. type URLResolver interface { GetBaseURL() string } diff --git a/api_public.go b/api_public.go index c453d4e..7d31558 100644 --- a/api_public.go +++ b/api_public.go @@ -28,7 +28,7 @@ func (api API) Handler() http.Handler { return api.router.Handler() } -//Router returns the specified router on an api instance +// Router returns the specified router on an api instance func (api API) Router() routing.Routeable { return api.router } diff --git a/examples/crud_example.go b/examples/crud_example.go index 15f6bbc..a0d28af 100644 --- a/examples/crud_example.go +++ b/examples/crud_example.go @@ -5,38 +5,51 @@ To play with this example server you can run some of the following curl requests In order to demonstrate dynamic baseurl handling for requests, apply the --header="REQUEST_URI:https://www.your.domain.example.com" parameter to any of the commands. Create a new user: + curl -X POST http://localhost:31415/v0/users -d '{"data" : {"type" : "users" , "attributes": {"user-name" : "marvin"}}}' List users: + curl -X GET http://localhost:31415/v0/users List paginated users: + curl -X GET 'http://localhost:31415/v0/users?page\[offset\]=0&page\[limit\]=2' + OR + curl -X GET 'http://localhost:31415/v0/users?page\[number\]=1&page\[size\]=2' Update: + curl -vX PATCH http://localhost:31415/v0/users/1 -d '{ "data" : {"type" : "users", "id": "1", "attributes": {"user-name" : "better marvin"}}}' Delete: + curl -vX DELETE http://localhost:31415/v0/users/2 Create a chocolate with the name sweet + curl -X POST http://localhost:31415/v0/chocolates -d '{"data" : {"type" : "chocolates" , "attributes": {"name" : "Ritter Sport", "taste": "Very Good"}}}' Create a user with a sweet + curl -X POST http://localhost:31415/v0/users -d '{"data" : {"type" : "users" , "attributes": {"user-name" : "marvin"}, "relationships": {"sweets": {"data": [{"type": "chocolates", "id": "1"}]}}}}' List a users sweets + curl -X GET http://localhost:31415/v0/users/1/sweets Replace a users sweets + curl -X PATCH http://localhost:31415/v0/users/1/relationships/sweets -d '{"data" : [{"type": "chocolates", "id": "2"}]}' Add a sweet + curl -X POST http://localhost:31415/v0/users/1/relationships/sweets -d '{"data" : [{"type": "chocolates", "id": "2"}]}' Remove a sweet + curl -X DELETE http://localhost:31415/v0/users/1/relationships/sweets -d '{"data" : [{"type": "chocolates", "id": "2"}]}' */ package main diff --git a/examples/resolver/resolver.go b/examples/resolver/resolver.go index 942b049..4ade39c 100644 --- a/examples/resolver/resolver.go +++ b/examples/resolver/resolver.go @@ -5,20 +5,20 @@ import ( "net/http" ) -//RequestURL simply returns -//the request url from REQUEST_URI header -//this should not be done in production applications +// RequestURL simply returns +// the request url from REQUEST_URI header +// this should not be done in production applications type RequestURL struct { r http.Request Port int } -//SetRequest to implement `RequestAwareResolverInterface` +// SetRequest to implement `RequestAwareResolverInterface` func (m *RequestURL) SetRequest(r http.Request) { m.r = r } -//GetBaseURL implements `URLResolver` interface +// GetBaseURL implements `URLResolver` interface func (m RequestURL) GetBaseURL() string { if uri := m.r.Header.Get("REQUEST_URI"); uri != "" { return uri diff --git a/examples/resource/resource_user.go b/examples/resource/resource_user.go index 22994d3..795a74b 100644 --- a/examples/resource/resource_user.go +++ b/examples/resource/resource_user.go @@ -152,7 +152,7 @@ func (s UserResource) Delete(id string, r api2go.Request) (api2go.Responder, err return &Response{Code: http.StatusNoContent}, err } -//Update stores all changes on the user +// Update stores all changes on the user func (s UserResource) Update(obj interface{}, r api2go.Request) (api2go.Responder, error) { user, ok := obj.(model.User) if !ok { diff --git a/go.mod b/go.mod index fed870f..98bcce4 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,45 @@ module github.com/retailnext/api2go -go 1.14 +go 1.18 require ( github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 - github.com/gin-gonic/gin v1.6.2 + github.com/gin-gonic/gin v1.7.7 github.com/gorilla/mux v1.7.4 github.com/julienschmidt/httprouter v1.3.0 github.com/labstack/echo v3.3.10+incompatible - github.com/labstack/gommon v0.4.0 // indirect github.com/manyminds/api2go v0.0.0-20220325145637-95b4fb838cf6 github.com/onsi/ginkgo v1.16.5 github.com/onsi/gomega v1.10.1 gopkg.in/guregu/null.v3 v3.4.0 ) +require ( + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.13.0 // indirect + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/go-playground/validator/v10 v10.4.1 // indirect + github.com/golang/protobuf v1.4.2 // indirect + github.com/json-iterator/go v1.1.9 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/leodido/go-urn v1.2.0 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect + github.com/nxadm/tail v1.4.8 // indirect + github.com/ugorji/go/codec v1.1.7 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.1 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.23.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect +) + replace github.com/manyminds/api2go v0.0.0-20220325145637-95b4fb838cf6 => ./ diff --git a/go.sum b/go.sum index f3326f0..c13daca 100644 --- a/go.sum +++ b/go.sum @@ -8,16 +8,16 @@ github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSG github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= -github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= @@ -73,7 +73,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= @@ -84,15 +83,17 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -108,12 +109,14 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/jsonapi/fixtures_test.go b/jsonapi/fixtures_test.go index 64d0eaa..a5e1cbe 100644 --- a/jsonapi/fixtures_test.go +++ b/jsonapi/fixtures_test.go @@ -498,7 +498,7 @@ type NumberPost struct { UnsignedNumber uint64 } -func (n NumberPost) GetID() Identifier{ +func (n NumberPost) GetID() Identifier { return Identifier{ID: n.ID, LID: n.LID} } diff --git a/jsonapi/unmarshal.go b/jsonapi/unmarshal.go index 930d5cf..0d7f30d 100644 --- a/jsonapi/unmarshal.go +++ b/jsonapi/unmarshal.go @@ -245,4 +245,4 @@ func checkType(incomingType string, target UnmarshalIdentifier) error { } return nil -} \ No newline at end of file +} diff --git a/routing/gingonic.go b/routing/gingonic.go index 9d6c12d..d57c581 100644 --- a/routing/gingonic.go +++ b/routing/gingonic.go @@ -30,7 +30,7 @@ func (g ginRouter) Handle(protocol, route string, handler HandlerFunc) { g.router.Handle(protocol, route, wrappedCallback) } -//Gin creates a new api2go router to use with the gin framework +// Gin creates a new api2go router to use with the gin framework func Gin(g *gin.Engine) Routeable { return &ginRouter{router: g} } diff --git a/routing/gorillamux.go b/routing/gorillamux.go index 996412f..51d0b05 100644 --- a/routing/gorillamux.go +++ b/routing/gorillamux.go @@ -39,7 +39,7 @@ func (gm gorillamuxRouter) Handle(protocol, route string, handler HandlerFunc) { gm.router.HandleFunc(modroute, wrappedHandler).Methods(protocol) } -//Gorilla creates a new api2go router to use with the Gorilla mux framework +// Gorilla creates a new api2go router to use with the Gorilla mux framework func Gorilla(gm *mux.Router) Routeable { return &gorillamuxRouter{router: gm} }