api2go icon indicating copy to clipboard operation
api2go copied to clipboard

api2go jsonapi client

Open pmccarren opened this issue 10 years ago • 11 comments

It would be awesome if we could use api2go as a golang client, accessing a jsonapi server. Most of the functionality is currently in place, however this seems like it makes sense to do this after the Unmarshal refactorization (#111) project is done.

We would need to add support for Unmarshaling 'Composer Documents': http://jsonapi.org/format/#document-structure-compound-documents

pmccarren avatar May 01 '15 16:05 pmccarren

+1 I'm very looking forward to this.

dopin avatar Oct 08 '15 08:10 dopin

good to know! Any special requirements we should keep in mind?

sharpner avatar Oct 08 '15 08:10 sharpner

Well, I'm a novice at Go, so I don't have any idea but well written documents would help users like me, I guess. I have JSON API written in Rails and just started to develop a CLI for that.

Oh I think it would be nice that api2go could do like this:

var posts []Post
body, err := ioutil.ReadAll(response.Body) // net/http
api2go.Unmarshal(body, posts)

dopin avatar Oct 08 '15 09:10 dopin

if you only have simple posts without compound objects, it should already work. Currently only our marshaller supports the generation of a json with compound objects included in it. We have to add this feature to the unmarshaller too.

wwwdata avatar Oct 08 '15 09:10 wwwdata

It has relationships in it. Is that what you mean by "compound objects"? I'm trying to unmarshal the data but I get []. :cry: I think I'm doing something wrong but I still don't know the right way.

dopin avatar Oct 08 '15 09:10 dopin

as long as you only have relationship IDs, it should work, because that is what works when you use it server-side. Compound objects are objects that you can find under the included key in the json. For example if you send a blog post including the comment objects.

wwwdata avatar Oct 08 '15 09:10 wwwdata

I'm sorry. I still can't understand. I wrote code like this.

type App struct {
    Id          string  `jsonapi:"primary,apps"`
    Name        string  `jsonapi:"attr,name"`
    CreatedAt   time.Time   `jsonapi:"attr,created_at"`
    UpdatedAt   time.Time   `jsonapi:"attr,updated_at"`
    Images      []*Image    `jsonapi:"relation,images"`
}

type Image struct {
    Id  string              `jsonapi:"-"`
    ImageName   string      `jsonapi:"-"`
    CreatedAt   time.Time   `jsonapi:"-"`
    UpdatedAt   time.Time   `jsonapi:"-"`
    App         App         `jsonapi:"-"`
}

func main() {
    client := &http.Client{}
     req, err := http.NewRequest("GET", "http://localhost:3000/apps", nil)
    resp, err := client.Do(req)
    body, err := ioutil.ReadAll(resp.Body)
}

How can I unmarshal the body ?

dopin avatar Oct 08 '15 10:10 dopin

are you sure that you are even using our package? we do not provide annotations that you posted in your code snippet. But this is getting offtopic for this ticket, please use our gitter chatroom or let's discuss this in another ticket.

wwwdata avatar Oct 08 '15 10:10 wwwdata

Oh, I'm sorry. I'll join the chat room.

dopin avatar Oct 08 '15 10:10 dopin

no worries ;) thx!

sharpner avatar Oct 08 '15 10:10 sharpner

now that we have refactored the Unmarshaling part we can think about this again too.

What needs to be done to support everything from a clients perspective is the support for unmarshaling of included/compound objects, right? So we need a way to specify more than the one target to unmarshal into. We need some good solution to pass multiple targets for expected included objects too. The unmarshaler will then unmarshal the correct objects in the correct structs or slices

I am thinking about something like this if you load a BlogPost that has an Author and Comments:

included := map[string]interface{}{
  "author": &Author{},
  "comments": []Comment{},
}

UnmarshalWithIncluded(json []byte, target interface{}, included map[string]interface{}) error

our main jsonapi Document looks like this currently:

type Document struct {
    Links    *Links                 `json:"links,omitempty"`
    Data     *DataContainer         `json:"data"`
    Included []Data                 `json:"included,omitempty"`
    Meta     map[string]interface{} `json:"meta,omitempty"`
}

So, included stuff is just a []Data and we can go through that and unmarshal it into the included map

type Data struct {
    Type          string                  `json:"type"`
    ID            string                  `json:"id"`
    Attributes    json.RawMessage         `json:"attributes"`
    Relationships map[string]Relationship `json:"relationships,omitempty"`
    Links         *Links                  `json:"links,omitempty"`
}

The Type field indicates which included Data struct must be unmarshalled into which struct/slice in the included map.

Does somebody have a better idea?

wwwdata avatar Dec 16 '15 14:12 wwwdata