Skip to content

Add links/meta to the top document #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mbn18 opened this issue Jun 14, 2017 · 6 comments
Closed

Add links/meta to the top document #93

mbn18 opened this issue Jun 14, 2017 · 6 comments
Assignees

Comments

@mbn18
Copy link

mbn18 commented Jun 14, 2017

Hi, I cant seems to create a response like the one bellow (origin JsonAPI site)

{
    "meta": {
        "total-pages": 13
    },
    "data": [{
        "type": "articles",
        "id": "3",
        "attributes": {
            "title": "JSON API paints my bikeshed!",
            "body": "The shortest article. Ever.",
            "created": "2015-05-22T14:56:29.000Z",
            "updated": "2015-05-22T14:56:28.000Z"
        }
    }],
    "links": {
        "self": "http://example.com/articles?page[number]=3&page[size]=1",
        "first": "http://example.com/articles?page[number]=1&page[size]=1",
        "prev": "http://example.com/articles?page[number]=2&page[size]=1",
        "next": "http://example.com/articles?page[number]=4&page[size]=1",
        "last": "http://example.com/articles?page[number]=13&page[size]=1"
    }
}

I can get the data but could not figure how to add meta or links to the response. I could only add them under the data level

This is the struct

type Album struct {
	ID   int    `jsonapi:"primary,album"`
	Name string `jsonapi:"attr,name"`
}

First I tried []*Album but coludnt understand how to add meta/links to the slice
Then I created a wrapper struct

type Albums struct {
	Albums []*Album `jsonapi:"relation,albums"`
}

But the output was invalid (or at least not like the provided example)

What am I missing. How can I add meta/links to the top level. Thanks.

@aren55555 aren55555 self-assigned this Jun 28, 2017
@aren55555
Copy link
Contributor

aren55555 commented Jun 28, 2017

You can do something like the following:

func main() {
	albums := []*Album{
		&Album{ID: 3, Name: "JSON API paints my bikeshed!"},
	}

	var m []interface{}
	for _, a := range albums {
		m = append(m, a)
	}
	payload, err := jsonapi.MarshalMany(m)
	if err != nil {
		panic(err)
	}

	payload.Meta = &jsonapi.Meta{
		"total-pages": 13,
	}

	payload.Links = &jsonapi.Links{
		"self":  "http://example.com/articles?page[number]=3&page[size]=1",
		"first": "http://example.com/articles?page[number]=1&page[size]=1",
		"prev":  "http://example.com/articles?page[number]=2&page[size]=1",
		"next":  "http://example.com/articles?page[number]=4&page[size]=1",
		"last":  "http://example.com/articles?page[number]=13&page[size]=1",
	}

	w := bytes.NewBuffer(nil)
	if err := json.NewEncoder(w).Encode(payload); err != nil {
		panic(err)
	}
	fmt.Println(w)
}

Which will print the following JSON:

{
    "data": [{
        "type": "album",
        "id": "3",
        "attributes": {
            "name": "JSON API paints my bikeshed!"
        }
    }],
    "links": {
        "first": "http://example.com/articles?page[number]=1&page[size]=1",
        "last": "http://example.com/articles?page[number]=13&page[size]=1",
        "next": "http://example.com/articles?page[number]=4&page[size]=1",
        "prev": "http://example.com/articles?page[number]=2&page[size]=1",
        "self": "http://example.com/articles?page[number]=3&page[size]=1"
    },
    "meta": {
        "total-pages": 13
    }
}

@imhuytq
Copy link

imhuytq commented Aug 3, 2017

@aren55555 Now, MarshalMany isn't public function, how can I use it?

@aren55555
Copy link
Contributor

aren55555 commented Aug 3, 2017

@pp4p since posting that sample code the Marshal API has been significantly simplified. You no longer need to populate a slice of interfaces []interface{}. Furthermore, Marshal first return will be a value that implements the Payloader interface. Since we passed Marshal a slice, the Payloader returned will always be a *jsonapi.ManyPayload.

You can now get away with just doing main.go:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/google/jsonapi"
)

type Album struct {
	ID   int    `jsonapi:"primary,album"`
	Name string `jsonapi:"attr,name"`
}

type Albums struct {
	Albums []*Album `jsonapi:"relation,albums"`
}

func main() {
	albums := []*Album{
		&Album{ID: 3, Name: "JSON API paints my bikeshed!"},
	}

	p, err := jsonapi.Marshal(albums)
	if err != nil {
		panic(err)
	}

	payload, ok := p.(*jsonapi.ManyPayload)
	if !ok {
		panic(fmt.Errorf("was not a many payloader"))
	}

	payload.Meta = &jsonapi.Meta{
		"total-pages": 13,
	}

	payload.Links = &jsonapi.Links{
		"self":  "http://example.com/articles?page[number]=3&page[size]=1",
		"first": "http://example.com/articles?page[number]=1&page[size]=1",
		"prev":  "http://example.com/articles?page[number]=2&page[size]=1",
		"next":  "http://example.com/articles?page[number]=4&page[size]=1",
		"last":  "http://example.com/articles?page[number]=13&page[size]=1",
	}

	w := bytes.NewBuffer(nil)
	if err := json.NewEncoder(w).Encode(payload); err != nil {
		panic(err)
	}
	fmt.Println(w)
}

go run main.go will produce:

{
    "data": [{
        "type": "album",
        "id": "3",
        "attributes": {
            "name": "JSON API paints my bikeshed!"
        }
    }],
    "links": {
        "first": "http://example.com/articles?page[number]=1\u0026page[size]=1",
        "last": "http://example.com/articles?page[number]=13\u0026page[size]=1",
        "next": "http://example.com/articles?page[number]=4\u0026page[size]=1",
        "prev": "http://example.com/articles?page[number]=2\u0026page[size]=1",
        "self": "http://example.com/articles?page[number]=3\u0026page[size]=1"
    },
    "meta": {
        "total-pages": 13
    }
}

@imhuytq
Copy link

imhuytq commented Aug 3, 2017

@aren55555 thank you.

@mrhwick
Copy link

mrhwick commented Nov 29, 2017

I thought about submitting a PR to implement this in the library, since the official site recommends top level links be included alongside resource-level and relationship links. I think it's important to keep the library simple, so I decided against writing this feature into the library. Would it make sense to write some documentation for how to achieve this recommended functionality.

@rberlind
Copy link

rberlind commented Nov 9, 2020

The solution provided here only addresses marshaling links into a structure. We also need a solution for unmarshalling links from a structure that uses jsonapi structure tags for other fields. Adding additional links/meta tags to the top document as the original poster requested would really make the jsonapi much easier to use. And while an example of the solution given in #68 is provided in https://github.com/google/jsonapi/blob/master/request_test.go#L694, that example does not show how to demarshal a structure that uses jsonapi structure tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants