Skip to content

Commit

Permalink
Merge pull request #202 from manyminds/internal-data-structs
Browse files Browse the repository at this point in the history
Major Refactoring: Fully typed structs that represent a jsonapi document
  • Loading branch information
sharpner committed Dec 11, 2015
2 parents 8fbbd12 + 4814f9b commit 0ee11bd
Show file tree
Hide file tree
Showing 31 changed files with 2,214 additions and 2,297 deletions.
101 changes: 76 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ In order to get updates in the future there are two ways to possible ways for yo

## TOC
- [Installation](#installation)
- [Basic funtionality](#basic-functionality)
- [Examples](#examples)
- [Interfaces to implement](#interfaces-to-implement)
- [Responder](#responder)
Expand All @@ -33,7 +34,6 @@ In order to get updates in the future there are two ways to possible ways for yo
- [UnmarshalIdentifier](#unmarshalidentifier)
- [Marshalling with References to other structs](#marshalling-with-references-to-other-structs)
- [Unmarshalling with references to other structs](#unmarshalling-with-references-to-other-structs)
- [Ignoring fields](#ignoring-fields)
- [Manual marshaling / unmarshaling](#manual-marshaling--unmarshaling)
- [SQL Null-Types](#sql-null-types)
- [Using api2go with the gin framework](#api2go-with-gin)
Expand All @@ -58,6 +58,54 @@ If you only need marshalling and/or unmarshalling:
go get gopkg.in/manyminds/api2go.v0/jsonapi
```

## Basic functionality
Api2go will Marshal/Unmarshal exactly like the internal `json` package from Go
with one addition: It will decorate the Marshaled json with jsonapi meta
objects. Jsonapi wraps the payload inside an `attributes` object. The rest is
just Meta-Data which will be generated by Api2go.

So let's take this basic example:

```go
type Article struct {
ID string
Title string `json:"title"`
}
```

Would `json.Marshal` into this Json:

```json
{
"ID": "Some-ID",
"title": "the title"
}
```

For api2go, you have to ignore tag the `ID` field and then the result could be
something like this:

```json
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
}
}
```

All the additional information is retrieved by implementing some interface
methods.

## Examples

Expand All @@ -70,16 +118,16 @@ comments that belong with a has-many relation to the post.

```go
type Post struct {
ID int `jsonapi:"-"` // Ignore ID field because the ID is fetched via the
// GetID() method and must not be inside the attributes object.
Title string
Comments []Comment `jsonapi:"-"` // this will be ignored by the api2go marshaller
CommentsIDs []int `jsonapi:"-"` // it's only useful for our internal relationship handling
ID int `json:"-"` // Ignore ID field because the ID is fetched via the
// GetID() method and must not be inside the attributes object.
Title string `json:"title"`
Comments []Comment `json:"-"` // this will be ignored by the api2go marshaller
CommentsIDs []int `json:"-"` // it's only useful for our internal relationship handling
}

type Comment struct {
ID int
Text string `jsonapi:"name=content"`
ID int `json:"-"`
Text string `json:"text"`
}
```

Expand All @@ -91,8 +139,7 @@ In the Post example struct, the `ID` field is ignored because api2go will use th
Every field inside a struct will be marshaled into the `attributes` object in
the json. In our example, we just want to have the `Title` field there.

In order to use different internal names for elements, you can specify a jsonapi tag. The api will marshal results now with the name in the tag.
Create/Update/Delete works accordingly, but will fallback to the internal value as well if possible.
Don't forget to name all your fields with the `json:"yourName"` tag.

### Responder
```go
Expand Down Expand Up @@ -210,10 +257,6 @@ type UnmarshalToManyRelations interface {

**If you need to know more about how to use the interfaces, look at our tests or at the example project.**

## Ignoring fields
api2go ignores all fields that are marked with the `jsonapi:"-"` ignore tag. This is useful if your struct has some more
fields which are only used internally to manage relations or data that needs to stay private, like a password field.

## Manual marshaling / unmarshaling
Please keep in mind that this only works if you implemented the previously mentioned interfaces. Manual marshalling and
unmarshalling makes sense, if you do not want to use our API that automatically generates all the necessary routes for you. You
Expand All @@ -224,7 +267,7 @@ comment1 = Comment{ID: 1, Text: "First!"}
comment2 = Comment{ID: 2, Text: "Second!"}
post = Post{ID: 1, Title: "Foobar", Comments: []Comment{comment1, comment2}}

json, err := jsonapi.MarshalToJSON(post)
json, err := jsonapi.Marshal(post)
```

will yield
Expand All @@ -235,6 +278,9 @@ will yield
{
"id": "1",
"type": "posts",
"attributes": {
"title": "Foobar"
},
"relationships": {
"comments": {
"data": [
Expand All @@ -246,43 +292,48 @@ will yield
"id": "2",
"type": "comments"
}
],
]
}
},
"title": "Foobar"
}
}
],
"included": [
{
"id": "1",
"type": "comments",
"text": "First!"
"attributes": {
"text": "First!"
}
},
{
"id": "2",
"type": "comments",
"text": "Second!"
"attributes": {
"text": "Second!"
}
}
]
}
```

You can also use `jsonapi.MarshalToJSONWithURLs` to automatically generate URLs for the rest endpoints that have a
You can also use `jsonapi.MarshalWithURLs` to automatically generate URLs for the rest endpoints that have a
version and BaseURL prefix. This will generate the same routes that our API uses. This adds `self` and `related` fields
for relations inside the `relationships` object.

Recover the structure from above using
Recover the structure from above using. Keep in mind that Unmarshaling with
included structs does not work yet. So Api2go cannot be used as a client yet.

```go
var posts []Post
err := jsonapi.UnmarshalFromJSON(json, &posts)
err := jsonapi.Unmarshal(json, &posts)
// posts[0] == Post{ID: 1, Title: "Foobar", CommentsIDs: []int{1, 2}}
```

If on the other hand you want to change the response encoding while still taking advantage of the autogenerated logic
you need to provide a `ContentMarshaler` for each `Content-Type` you would like to support.

The `ContentMarshaler` interface declares two functions
The `ContentMarshaler` interface declares two functions **deprecated, we have
to figure out what to do with this yet**

```go
type ContentMarshaler interface {
Expand Down Expand Up @@ -496,7 +547,7 @@ all query parameters as `map[string][]string` unfiltered. So you can use it for:
```go
type fixtureSource struct {}

func (s *fixtureSource) FindAll(req api2go.Request) (interface{}, error) {
func (s *fixtureSource) FindAll(req api2go.Request) (Responder, error) {
for key, values range req.QueryParams {
...
}
Expand Down
Loading

0 comments on commit 0ee11bd

Please sign in to comment.