Documentation
¶
Overview ¶
Example ¶
package main
import (
"context"
"net/http"
"time"
"github.com/dolanor/rip/encoding/html"
"github.com/dolanor/rip/encoding/json"
)
func main() {
up := newUserProvider()
ro := NewRouteOptions().
WithCodecs(json.Codec, html.Codec)
http.HandleFunc(HandleEntities("/users/", up, ro))
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}
type user struct {
Name string `json:"name" xml:"name"`
EmailAddress string `json:"email_address" xml:"email_address"`
BirthDate time.Time `json:"birth_date" xml:"birth_date"`
}
func (u user) IDString() string {
return u.Name
}
func (u *user) IDFromString(s string) error {
u.Name = s
return nil
}
type UserProvider struct {
mem map[string]user
}
func newUserProvider() *UserProvider {
return &UserProvider{
mem: map[string]user{},
}
}
func (up *UserProvider) Create(ctx context.Context, u *user) (*user, error) {
up.mem[u.Name] = *u
return u, nil
}
func (up UserProvider) Get(ctx context.Context, entity Entity) (*user, error) {
u, ok := up.mem[entity.IDString()]
if !ok {
return &user{}, ErrNotFound
}
return &u, nil
}
func (up *UserProvider) Delete(ctx context.Context, entity Entity) error {
_, ok := up.mem[entity.IDString()]
if !ok {
return ErrNotFound
}
delete(up.mem, entity.IDString())
return nil
}
func (up *UserProvider) Update(ctx context.Context, u *user) error {
_, ok := up.mem[u.Name]
if !ok {
return ErrNotFound
}
up.mem[u.Name] = *u
return nil
}
func (up *UserProvider) ListAll(ctx context.Context) ([]*user, error) {
var users []*user
for _, u := range up.mem {
// we copy to avoid referring the same pointer that would get updated
u := u
users = append(users, &u)
}
return users, nil
}
Index ¶
- Constants
- Variables
- func Handle[Input, Output any](method string, f InputOutputFunc[Input, Output], options *RouteOptions) http.HandlerFunc
- func HandleEntities[Ent Entity, EP EntityProvider[Ent]](urlPath string, ep EP, options *RouteOptions) (path string, handler http.HandlerFunc)
- type Entity
- type EntityCreater
- type EntityDeleter
- type EntityGetter
- type EntityLister
- type EntityProvider
- type EntityUpdater
- type Error
- type ErrorCode
- type ErrorSource
- type InputOutputFunc
- type Link
- type Middleware
- type RouteOptions
Examples ¶
Constants ¶
const NewEntityID = "rip-new-entity-id"
Variables ¶
var ( // ErrNotFound represents when a resource is not found. // It can also be used if a user without proper authorization // should not know if a resource exists or not. ErrNotFound = Error{ Code: ErrorCodeNotFound, Status: http.StatusNotFound, Detail: "entity not found", } // ErrNotImplemented communicates if a specific entity function is not // implemented. ErrNotImplemented = Error{ Code: ErrorCodeNotImplemented, Status: http.StatusNotImplemented, Detail: "not implemented", } )
Functions ¶
func Handle ¶
func Handle[ Input, Output any, ]( method string, f InputOutputFunc[Input, Output], options *RouteOptions, ) http.HandlerFunc
Handle is a generic HTTP handler that maps an HTTP method to a InputOutputFunc f.
func HandleEntities ¶ added in v0.1.4
func HandleEntities[ Ent Entity, EP EntityProvider[Ent], ]( urlPath string, ep EP, options *RouteOptions, ) (path string, handler http.HandlerFunc)
HandleEntities associates an urlPath with an entity provider, and handles all HTTP requests in a RESTful way:
POST /entities/ : creates the entity GET /entities/:id : get the entity PUT /entities/:id : updates the entity (needs to pass the full entity data) DELETE /entities/:id : deletes the entity GET /entities/ : lists the entities
Types ¶
type Entity ¶ added in v0.1.4
type Entity interface {
// IDString returns an ID in form of a string.
IDString() string
// IDFromString serialize an ID from s.
IDFromString(s string) error
}
Entity is a resource that can be identified by a string. It comes from the concept of entity in Domain Driven Design.
type EntityCreater ¶ added in v0.1.4
EntityCreater creates a resource that can be identified (an entity).
type EntityDeleter ¶ added in v0.1.4
EntityDeleter deletes a entity with its id.
type EntityGetter ¶ added in v0.1.4
EntityGetter gets a entity with its id.
type EntityLister ¶ added in v0.1.4
EntityLister lists a group of entities.
type EntityProvider ¶ added in v0.1.4
type EntityProvider[Ent Entity] interface { EntityCreater[Ent] EntityGetter[Ent] EntityUpdater[Ent] EntityDeleter[Ent] EntityLister[Ent] }
EntityProvider provides identifiable resources.
type EntityUpdater ¶ added in v0.1.4
EntityUpdater updates an entity.
type Error ¶
type Error struct {
// ID is a unique identifier for this particular occurrence of the problem.
ID string `json:"id,omitempty"`
// Links can contains an About Link or a Type Link.
Links []Link `json:"links,omitempty"`
// Status is the HTTP status code applicable to this problem. This SHOULD be provided.
Status int `json:"status,omitempty"`
// Code is an application-specific error code.
Code ErrorCode `json:"code,omitempty"`
// Title is a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
Title string `json:"title,omitempty"`
// Detail is a human-readable explanation specific to this occurrence of the problem
Detail string `json:"detail,omitempty"`
// Source is an object containing references to the primary source of the error. It SHOULD include one of its member or be omitted.
Source ErrorSource `json:"source,omitempty"`
}
Error is the error returned by rip. It is inspired by JSON-API.
type ErrorCode ¶
type ErrorCode int
ErrorCode maps errors from the ResourceProvider implementation to HTTP status code.
const ( // ErrorCodeNotFound happens when a resource with an id is not found. ErrorCodeNotFound ErrorCode = http.StatusNotFound // ErrorCodeNotImplemented is when the endpoint is not implemented. ErrorCodeNotImplemented ErrorCode = http.StatusNotImplemented // ErrorCodeBadQArg happens when a user gives a wrongly formatted header `; q=X.Y` argument. ErrorCodeBadQArg ErrorCode = 499 )
type ErrorSource ¶ added in v0.2.0
type ErrorSource struct {
// Pointer is a JSON Pointer [RFC6901] to the value in the request document
// that caused the error [e.g. "/data" for a primary data object,
// or "/data/attributes/title" for a specific attribute].
// This MUST point to a value in the request document that exists;
// if it doesn’t, the client SHOULD simply ignore the pointer.
Pointer string `json:"pointer,omitempty"`
// Parameter indicates which URI query parameter caused the error.
Parameter string `json:"parameter,omitempty"`
// Header indicates the name of a single request header which caused the error.
Header string `json:"header,omitempty"`
}
ErrorSource indicates the source error. It is based on the JSON API specification: https://jsonapi.org/format/#error-objects
type InputOutputFunc ¶ added in v0.1.4
type InputOutputFunc[ Input, Output any, ] func(ctx context.Context, input Input) (output Output, err error)
InputOutputFunc is a function that takes a ctx and an input, and it can return an output or an err.
type Link ¶ added in v0.2.0
type Link struct {
// HRef is a URI-reference [RFC3986 Section 4.1] pointing to the link’s target.
HRef string `json:"href,omitempty"`
// Rel indicates the link’s relation type. The string MUST be a valid link relation type.
Rel string `json:"rel,omitempty"`
// DescribedBy is a link to a description document (e.g. OpenAPI or JSON Schema) for the link target.
DescribedBy *Link `json:"describedby,omitempty"`
// Title serves as a label for the destination of a link such that it can be used as a human-readable identifier (e.g., a menu entry).
Title string `json:"title,omitempty"`
// Type indicates the media type of the link’s target.
Type string `json:"type,omitempty"`
// HRefLang indicates the language(s) of the link’s target. An array of strings indicates that the link’s target is available in multiple languages. Each string MUST be a valid language tag [RFC5646].
HRefLang []string `json:"hreflang,omitempty"`
}
Link represents a RFC8288 web link.
type Middleware ¶ added in v0.1.4
type Middleware func(http.HandlerFunc) http.HandlerFunc
Middleware is an HTTP Middleware that you can add to your handler to handle specific actions like logging, authentication, authorization, metrics, ….
type RouteOptions ¶ added in v0.2.0
type RouteOptions struct {
// contains filtered or unexported fields
}
RouteOptions allows to pass options to the route handler. It make each route able to have its own set of middlewares or codecs. It also allows to be reused betwenn multiple routes.
func NewRouteOptions ¶ added in v0.2.0
func NewRouteOptions() *RouteOptions
func (*RouteOptions) WithCodecs ¶ added in v0.2.0
func (ro *RouteOptions) WithCodecs(codecs ...encoding.Codec) *RouteOptions
func (*RouteOptions) WithMiddlewares ¶ added in v0.2.0
func (ro *RouteOptions) WithMiddlewares(middlewares ...Middleware) *RouteOptions