sakila/internal/hal/hal.go

89 lines
2.1 KiB
Go
Raw Normal View History

package hal
import (
"encoding/json"
"github.com/raff/halgo"
)
// Resource is a simple HAL resource envelope that uses halgo.Link for links.
type Resource struct {
Links map[string]halgo.Link `json:"_links,omitempty"`
Embedded map[string]interface{} `json:"_embedded,omitempty"`
// State holds arbitrary resource state merged at top-level when marshaled
State map[string]interface{} `json:"-"`
}
// New creates a new HAL resource with a self link.
func New(selfHref string) *Resource {
r := &Resource{
Links: map[string]halgo.Link{},
Embedded: map[string]interface{}{},
State: map[string]interface{}{},
}
if selfHref != "" {
r.AddLink("self", halgo.Link{Href: selfHref})
}
return r
}
// AddLink adds a link with the given rel.
func (r *Resource) AddLink(rel string, link halgo.Link) {
if r.Links == nil {
r.Links = map[string]halgo.Link{}
}
r.Links[rel] = link
}
// AddCurie adds a CURIE link for the sakila namespace.
// Note: HAL recommends an array for the "curies" rel. This implementation uses a single link
// due to the underlying link map being single-valued per rel. Most clients accept this form.
func (r *Resource) AddCurie() {
r.AddLink("curies", halgo.Link{
Href: "/rels/{rel}",
Templated: true,
Name: "sakila",
})
}
// Embed sets an embedded payload under the rel.
func (r *Resource) Embed(rel string, v interface{}) {
if r.Embedded == nil {
r.Embedded = map[string]interface{}{}
}
r.Embedded[rel] = v
}
// SetState sets or merges state fields.
func (r *Resource) SetState(obj interface{}) {
switch m := obj.(type) {
case map[string]interface{}:
for k, v := range m {
r.State[k] = v
}
default:
// marshal into map and merge
b, _ := json.Marshal(obj)
var mm map[string]interface{}
_ = json.Unmarshal(b, &mm)
for k, v := range mm {
r.State[k] = v
}
}
}
// MarshalJSON merges State with _links and _embedded following HAL conventions.
func (r *Resource) MarshalJSON() ([]byte, error) {
m := map[string]interface{}{}
for k, v := range r.State {
m[k] = v
}
if len(r.Links) > 0 {
m["_links"] = r.Links
}
if len(r.Embedded) > 0 {
m["_embedded"] = r.Embedded
}
return json.Marshal(m)
}