halgo

package module
v0.0.0-...-d1d6fd6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2015 License: MIT Imports: 10 Imported by: 1

README

halgo

HAL implementation in Go.

HAL is a simple format that gives a consistent and easy way to hyperlink between resources in your API.

Halgo helps with generating HAL-compliant JSON from Go structs, and provides a Navigator for walking a HAL-compliant API.

GoDoc

Install

go get github.com/jagregory/halgo

Usage

Serialising a resource with HAL links:

import "github.com/jagregory/halgo"

type MyResource struct {
  halgo.Links
  Name string
}

res := MyResource{
  Links: Links{}.
    Self("/orders").
    Next("/orders?page=2").
    Link("ea:find", "/orders{?id}").
    Add("ea:admin", Link{Href: "/admins/2", Title: "Fred"}, Link{Href: "/admins/5", Title: "Kate"}),
  Name: "James",
}

bytes, _ := json.Marshal(res)

fmt.Println(bytes)

// {
//   "_links": {
//     "self": { "href": "/orders" },
//     "next": { "href": "/orders?page=2" },
//     "ea:find": { "href": "/orders{?id}", "templated": true },
//     "ea:admin": [{
//         "href": "/admins/2",
//         "title": "Fred"
//     }, {
//         "href": "/admins/5",
//         "title": "Kate"
//     }]
//   },
//   "Name": "James"
// }

Navigating a HAL-compliant API:

res, err := halgo.Navigator("http://example.com").
  Follow("products").
  Followf("page", halgo.P{"n": 10}).
  Get()

Deserialising a resource:

import "github.com/jagregory/halgo"

type MyResource struct {
  halgo.Links
  Name string
}

data := []byte(`{
  "_links": {
    "self": { "href": "/orders" },
    "next": { "href": "/orders?page=2" },
    "ea:find": { "href": "/orders{?id}", "templated": true },
    "ea:admin": [{
        "href": "/admins/2",
        "title": "Fred"
    }, {
        "href": "/admins/5",
        "title": "Kate"
    }]
  },
  "Name": "James"
}`)

res := MyResource{}
json.Unmarshal(data, &res)

res.Name // "James"
res.Links.Href("self") // "/orders"
res.Links.HrefParams("self", Params{"id": 123}) // "/orders?id=123"

TODO

  • Curies
  • Embedded resources

Documentation

Overview

Package halgo is used to create application/hal+json representations of Go structs, and provides a client for navigating HAL-compliant APIs.

There are two sides to halgo: serialisation and navigation.

Serialisation is based around the Links struct, which you can embed in your own structures to provide HAL compliant links when you serialise your structs into JSON. Links has a little builder API which can make it somewhat more succinct to generate these links than modelling the structures yourself.

Navigation, specifically through the Navigator func, is for when you want to consume a HAL-compliant API and walk its relations.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Navigator(uri string) navigator

Navigator is a mechanism for navigating HAL-compliant REST APIs. You start by creating a Navigator with a base URI, then Follow the links exposed by the API until you reach the place where you want to perform an action.

For example, to request an API exposed at api.example.com and follow a link named products and GET the resulting page you'd do this:

res, err := Navigator("http://api.example.com").
  Follow("products").
  Get()

To do the same thing but POST to the products page, you'd do this:

res, err := Navigator("http://api.example.com").
  Follow("products").
  Post("application/json", someContent)

Multiple links followed in sequence.

res, err := Navigator("http://api.example.com").
  Follow("products").
  Follow("next")
  Get()

Links can also be expanded with Followf if they are URI templates.

res, err := Navigator("http://api.example.com").
  Follow("products").
  Followf("page", halgo.P{"number": 10})
  Get()

Navigation of relations is lazy. Requests will only be triggered when you execute a method which returns a result. For example, this doesn't perform any HTTP requests.

Navigator("http://api.example.com").
  Follow("products")

It's only when you add a call to Get, Post, PostForm, Patch, or Unmarshal to the end will any requests be triggered.

By default a Navigator will use http.DefaultClient as its mechanism for making HTTP requests. If you want to supply your own HttpClient, you can assign to nav.HttpClient after creation.

nav := Navigator("http://api.example.com")
nav.HttpClient = MyHttpClient{}

Any Client you supply must implement halgo.HttpClient, which http.Client does implicitly. By creating decorators for the HttpClient, logging and caching clients are trivial. See LoggingHttpClient for an example.

Example
var me struct{ Username string }

halgo.Navigator("http://haltalk.herokuapp.com/").
	Followf("ht:me", halgo.P{"name": "jagregory"}).
	Unmarshal(&me)

fmt.Println(me.Username)
Output:

jagregory
Example (Logging)
var me struct{ Username string }

nav := halgo.Navigator("http://haltalk.herokuapp.com/")
nav.HttpClient = halgo.LoggingHttpClient{http.DefaultClient}

nav.Followf("ht:me", halgo.P{"name": "jagregory"}).
	Unmarshal(&me)

fmt.Printf("Username: %s", me.Username)
Output:

GET http://haltalk.herokuapp.com/
GET http://haltalk.herokuapp.com/users/jagregory
Username: jagregory

Types

type HttpClient

type HttpClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HttpClient exposes Do from net/http Client.

type InvalidUrlError

type InvalidUrlError struct {
	// contains filtered or unexported fields
}

InvalidUrlError is returned when a link contains a malformed or invalid url.

func (InvalidUrlError) Error

func (err InvalidUrlError) Error() string
type Link struct {
	// The "href" property is REQUIRED.
	// Its value is either a URI [RFC3986] or a URI Template [RFC6570].
	// If the value is a URI Template then the Link Object SHOULD have a
	// "templated" attribute whose value is true.
	Href string `json:"href"`

	// The "templated" property is OPTIONAL.
	// Its value is boolean and SHOULD be true when the Link Object's "href"
	// property is a URI Template.
	// Its value SHOULD be considered false if it is undefined or any other
	// value than true.
	Templated bool `json:"templated,omitempty"`

	// The "type" property is OPTIONAL.
	// Its value is a string used as a hint to indicate the media type
	// expected when dereferencing the target resource.
	Type string `json:"type,omitempty"`

	// The "deprecation" property is OPTIONAL.
	// Its presence indicates that the link is to be deprecated (i.e.
	// removed) at a future date.  Its value is a URL that SHOULD provide
	// further information about the deprecation.
	// A client SHOULD provide some notification (for example, by logging a
	// warning message) whenever it traverses over a link that has this
	// property.  The notification SHOULD include the deprecation property's
	// value so that a client manitainer can easily find information about
	// the deprecation.
	Deprecation string `json:"deprecation,omitempty"`

	// The "name" property is OPTIONAL.
	// Its value MAY be used as a secondary key for selecting Link Objects
	// which share the same relation type.
	Name string `json:"name,omitempty"`

	// The "profile" property is OPTIONAL.
	// Its value is a string which is a URI that hints about the profile (as
	// defined by [I-D.wilde-profile-link]) of the target resource.
	Profile string `json:"profile,omitempty"`

	// The "title" property is OPTIONAL.
	// Its value is a string and is intended for labelling the link with a
	// human-readable identifier (as defined by [RFC5988]).
	Title string `json:"title,omitempty"`

	// The "hreflang" property is OPTIONAL.
	// Its value is a string and is intended for indicating the language of
	// the target resource (as defined by [RFC5988]).
	HrefLang string `json:"hreflang,omitempty"`
}

Link represents a HAL link

func (Link) Expand

func (l Link) Expand(params P) (string, error)

Expand will expand the URL template of the link with the given params.

type LinkNotFoundError

type LinkNotFoundError struct {
	// contains filtered or unexported fields
}

LinkNotFoundError is returned when a link with the specified relation couldn't be found in the links collection.

func (LinkNotFoundError) Error

func (err LinkNotFoundError) Error() string
type Links struct {
	Items map[string]linkSet `json:"_links,omitempty"`
}

Links represents a collection of HAL links. You can embed this struct in your own structs for sweet, sweet HAL serialisation goodness.

type MyStruct struct {
  halgo.Links
}

my := MyStruct{
  Links: halgo.Links{}.
    Self("http://example.com/").
    Next("http://example.com/1"),
}

func (Links) Add

func (l Links) Add(rel string, links ...Link) Links

Add creates multiple links with the same relation.

Add("abc", halgo.Link{Href: "/a/1"}, halgo.Link{Href: "/a/2"})

func (Links) Href

func (l Links) Href(rel string) (string, error)

Href tries to find the href of a link with the supplied relation. Returns LinkNotFoundError if a link doesn't exist.

func (Links) HrefParams

func (l Links) HrefParams(rel string, params P) (string, error)

HrefParams tries to find the href of a link with the supplied relation, then expands any URI template parameters. Returns LinkNotFoundError if a link doesn't exist.

func (l Links) Link(rel, href string, args ...interface{}) Links

Link creates a link with a named rel. Optionally can act as a format string with parameters.

Link("abc", "http://example.com/a/1")
Link("abc", "http://example.com/a/%d", id)

func (Links) Next

func (l Links) Next(href string, args ...interface{}) Links

Next creates a link with the rel as "next". Optionally can act as a format string with parameters.

Next("http://example.com/a/1")
Next("http://example.com/a/%d", id)

func (Links) Prev

func (l Links) Prev(href string, args ...interface{}) Links

Prev creates a link with the rel as "prev". Optionally can act as a format string with parameters.

Prev("http://example.com/a/1")
Prev("http://example.com/a/%d", id)

func (Links) Self

func (l Links) Self(href string, args ...interface{}) Links

Self creates a link with the rel as "self". Optionally can act as a format string with parameters.

Self("http://example.com/a/1")
Self("http://example.com/a/%d", id)

type LoggingHttpClient

type LoggingHttpClient struct {
	HttpClient
}

LoggingHttpClient is an example HttpClient implementation which wraps an existing HttpClient and prints the request URL to STDOUT whenever one occurs.

func (LoggingHttpClient) Do

type P

type P map[string]interface{}

P is a parameters map for expanding URL templates.

halgo.P{"id": 1}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL