res

package module
v0.4.7 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2023 License: MIT Imports: 13 Imported by: 22

README

Resgate logo

RES Service for Go
Synchronize Your Clients

License Report Card Build Status Coverage Reference


Go package used to create REST, real time, and RPC APIs, where all your reactive web clients are synchronized seamlessly through Resgate.

Visit Resgate.io for more information.

Installation

go get github.com/jirenius/go-res

As easy as

package main

import res "github.com/jirenius/go-res"

func main() {
   s := res.NewService("example")
   s.Handle("model",
      res.Access(res.AccessGranted),
      res.GetModel(func(r res.ModelRequest) {
         r.Model(struct {
            Message string `json:"message"`
         }{"Hello, World!"})
      }),
   )
   s.ListenAndServe("nats://localhost:4222")
}

Prerequisite

Install NATS Server and Resgate. Can be done with 3 docker commands:

docker network create res
docker run -d --name nats -p 4222:4222 --net res nats
docker run --name resgate -p 8080:8080 --net res resgateio/resgate --nats nats://nats:4222

Examples

Example Description
Hello World Smallest of services serving a static message.
Edit Text Single text field that is updated in real time.
Book Collection List of book titles & authors that can be edited by many.
Book Collection Store Book Collection example persisting changes using BadgerBD store.
Search Query Make live queries against a large customer database.

Note

Above examples are complete with both service and client.

Usage

Create a new service
s := res.NewService("myservice")
Add handlers for a model resource
mymodel := map[string]interface{}{"name": "foo", "value": 42}
s.Handle("mymodel",
   res.Access(res.AccessGranted),
   res.GetModel(func(r res.ModelRequest) {
      r.Model(mymodel)
   }),
)
Add handlers for a collection resource
mycollection := []string{"first", "second", "third"}
s.Handle("mycollection",
   res.Access(res.AccessGranted),
   res.GetCollection(func(r res.CollectionRequest) {
      r.Collection(mycollection)
   }),
)
Add handlers for parameterized resources
s.Handle("article.$id",
   res.Access(res.AccessGranted),
   res.GetModel(func(r res.ModelRequest) {
      article := getArticle(r.PathParam("id"))
      if article == nil {
         r.NotFound()
      } else {
         r.Model(article)
      }
   }),
)
Add handlers for method calls
s.Handle("math",
   res.Access(res.AccessGranted),
   res.Call("double", func(r res.CallRequest) {
      var p struct {
         Value int `json:"value"`
      }
      r.ParseParams(&p)
      r.OK(p.Value * 2)
   }),
)
Send change event on model update

A change event will update the model on all subscribing clients.

s.With("myservice.mymodel", func(r res.Resource) {
   mymodel["name"] = "bar"
   r.ChangeEvent(map[string]interface{}{"name": "bar"})
})
Send add event on collection update:

An add event will update the collection on all subscribing clients.

s.With("myservice.mycollection", func(r res.Resource) {
   mycollection = append(mycollection, "fourth")
   r.AddEvent("fourth", len(mycollection)-1)
})
Add handlers for authentication
s.Handle("myauth",
   res.Auth("login", func(r res.AuthRequest) {
      var p struct {
         Password string `json:"password"`
      }
      r.ParseParams(&p)
      if p.Password != "mysecret" {
         r.InvalidParams("Wrong password")
      } else {
         r.TokenEvent(map[string]string{"user": "admin"})
         r.OK(nil)
      }
   }),
)
Add handlers for access control
s.Handle("mymodel",
   res.Access(func(r res.AccessRequest) {
      var t struct {
         User string `json:"user"`
      }
      r.ParseToken(&t)
      if t.User == "admin" {
         r.AccessGranted()
      } else {
         r.AccessDenied()
      }
   }),
   res.GetModel(func(r res.ModelRequest) {
      r.Model(mymodel)
   }),
)
Using routes
s.Route("v2", func(m *res.Mux) {
   m.Handle("mymodel",
      /* ... */
   )
})
Start service
s.ListenAndServe("nats://localhost:4222")

Testing Reference

The restest subpackage is used for testing services and validate responses.

Inter-service communication Reference

The resprot subpackage provides low level structs and methods for communicating with other services over NATS server.

Storage Reference

The store subpackage contains handlers and interfaces for working with database storage.

Name Description Documentation
mockstore Mock store implementation for testing Reference
badgerstore BadgerDB store implementation Reference

Credits

Inspiration on the go-res API has been taken from github.com/go-chi/chi, a great package when writing ordinary HTTP services, and will continue to do so when it is time to implement Middleware, sub-handlers, and mounting.

Contributing

The go-res package is still under development, but the API is mostly settled. Any feedback on the package API or its implementation is highly appreciated!

Once the API is fully settled, the package will be moved to the resgateio GitHub organization.

If you find any issues, feel free to report them as an issue.

Documentation

Overview

Package res is used to create REST, real time, and RPC APIs, where all your reactive web clients are synchronized seamlessly through Resgate:

https://github.com/resgateio/resgate

The implementation provides structs and methods for creating services that listen to requests and send events over NATS server.

Concurrency

Requests are handled concurrently for multiple resources, but the package guarantees that only one goroutine is executing handlers for any unique resource at any one time. This allows handlers to modify models and collections without additional synchronization such as mutexes.

Usage

Create a new service:

s := res.NewService("myservice")

Add handlers for a model resource:

mymodel := map[string]interface{}{"name": "foo", "value": 42}
s.Handle("mymodel",
	res.Access(res.AccessGranted),
	res.GetModel(func(r res.ModelRequest) {
		r.Model(mymodel)
	}),
)

Add handlers for a collection resource:

mycollection := []string{"first", "second", "third"}
s.Handle("mycollection",
	res.Access(res.AccessGranted),
	res.GetCollection(func(r res.CollectionRequest) {
		r.Collection(mycollection)
	}),
)

Add handlers for parameterized resources:

s.Handle("article.$id",
	res.Access(res.AccessGranted),
	res.GetModel(func(r res.ModelRequest) {
		article := getArticle(r.PathParam("id"))
		if article == nil {
			r.NotFound()
		} else {
			r.Model(article)
		}
	}),
)

Add handlers for method calls:

s.Handle("math",
	res.Access(res.AccessGranted),
	res.Call("double", func(r res.CallRequest) {
		var p struct {
			Value int `json:"value"`
		}
		r.ParseParams(&p)
		r.OK(p.Value * 2)
	}),
)

Send change event on model update:

s.With("myservice.mymodel", func(r res.Resource) {
	mymodel["name"] = "bar"
	r.ChangeEvent(map[string]interface{}{"name": "bar"})
})

Send add event on collection update:

s.With("myservice.mycollection", func(r res.Resource) {
	mycollection = append(mycollection, "fourth")
	r.AddEvent("fourth", len(mycollection)-1)
})

Add handlers for authentication:

s.Handle("myauth",
	res.Auth("login", func(r res.AuthRequest) {
		var p struct {
			Password string `json:"password"`
		}
		r.ParseParams(&p)
		if p.Password != "mysecret" {
			r.InvalidParams("Wrong password")
		} else {
			r.TokenEvent(map[string]string{"user": "admin"})
			r.OK(nil)
		}
	}),
)

Add handlers for access control:

s.Handle("mymodel",
	res.Access(func(r res.AccessRequest) {
		var t struct {
			User string `json:"user"`
		}
		r.ParseToken(&t)
		if t.User == "admin" {
			r.AccessGranted()
		} else {
			r.AccessDenied()
		}
	}),
	res.GetModel(func(r res.ModelRequest) {
		r.Model(mymodel)
	}),
)

Start service:

s.ListenAndServe("nats://localhost:4222")

Index

Constants

View Source
const (
	CodeAccessDenied   = "system.accessDenied"
	CodeInternalError  = "system.internalError"
	CodeInvalidParams  = "system.invalidParams"
	CodeInvalidQuery   = "system.invalidQuery"
	CodeMethodNotFound = "system.methodNotFound"
	CodeNotFound       = "system.notFound"
	CodeTimeout        = "system.timeout"
)

Predefined error codes

View Source
const (
	RequestTypeAccess = "access"
	RequestTypeGet    = "get"
	RequestTypeCall   = "call"
	RequestTypeAuth   = "auth"
)

Request types

Variables

View Source
var (
	ErrAccessDenied   = &Error{Code: CodeAccessDenied, Message: "Access denied"}
	ErrInternalError  = &Error{Code: CodeInternalError, Message: "Internal error"}
	ErrInvalidParams  = &Error{Code: CodeInvalidParams, Message: "Invalid parameters"}
	ErrInvalidQuery   = &Error{Code: CodeInvalidQuery, Message: "Invalid query"}
	ErrMethodNotFound = &Error{Code: CodeMethodNotFound, Message: "Method not found"}
	ErrNotFound       = &Error{Code: CodeNotFound, Message: "Not found"}
	ErrTimeout        = &Error{Code: CodeTimeout, Message: "Request timeout"}
)

Predefined errors

View Source
var (
	// Model sets handler type to model
	Model = OptionFunc(func(hs *Handler) {
		if hs.Type != TypeUnset {
			panic("res: resource type set multiple times")
		}
		hs.Type = TypeModel
	})

	// Collection sets handler type to collection
	Collection = OptionFunc(func(hs *Handler) {
		if hs.Type != TypeUnset {
			panic("res: resource type set multiple times")
		}
		hs.Type = TypeCollection
	})
)
View Source
var DeleteAction = &struct{ json.RawMessage }{RawMessage: json.RawMessage(`{"action":"delete"}`)}

DeleteAction is used for deleted properties in "change" events

Functions

func IsValidRID added in v0.4.0

func IsValidRID(rid string) bool

IsValidRID returns true if the resource ID is valid, otherwise false.

Types

type AccessHandler

type AccessHandler func(AccessRequest)

AccessHandler is a function called on resource access requests

var (
	// AccessGranted is an access handler that provides full get and call access.
	AccessGranted AccessHandler = func(r AccessRequest) {
		r.AccessGranted()
	}

	// AccessDenied is an access handler that sends a response denying all access.
	AccessDenied AccessHandler = func(r AccessRequest) {
		r.AccessDenied()
	}
)

Predefined handlers

type AccessRequest

type AccessRequest interface {
	Resource
	CID() string
	RawToken() json.RawMessage
	ParseToken(interface{})
	Access(get bool, call string)
	AccessDenied()
	AccessGranted()
	NotFound()
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
}

AccessRequest has methods for responding to access requests.

type ApplyAddHandler

type ApplyAddHandler func(r Resource, value interface{}, idx int) error

ApplyAddHandler is a function called to apply a collection add event. Must return an error if the add event couldn't be applied to the resource.

type ApplyChangeHandler

type ApplyChangeHandler func(r Resource, changes map[string]interface{}) (map[string]interface{}, error)

ApplyChangeHandler is a function called to apply a model change event. Must return a map with the values to apply to revert the changes, or error.

type ApplyCreateHandler

type ApplyCreateHandler func(r Resource, data interface{}) error

ApplyCreateHandler is a function called to apply a resource create event. Must return an error if the resource couldn't be created.

type ApplyDeleteHandler

type ApplyDeleteHandler func(r Resource) (interface{}, error)

ApplyDeleteHandler is a function called to apply a resource delete event. Must return the resource data being removed, or error.

type ApplyRemoveHandler

type ApplyRemoveHandler func(r Resource, idx int) (interface{}, error)

ApplyRemoveHandler is a function called to apply a collection remove event. Must return the value being removed, or error.

type AuthHandler

type AuthHandler func(AuthRequest)

AuthHandler is a function called on resource auth requests

type AuthRequest

type AuthRequest interface {
	Resource
	Method() string
	CID() string
	RawParams() json.RawMessage
	RawToken() json.RawMessage
	ParseParams(interface{})
	ParseToken(interface{})
	Header() map[string][]string
	Host() string
	RemoteAddr() string
	URI() string
	OK(result interface{})
	Resource(rid string)
	NotFound()
	MethodNotFound()
	InvalidParams(message string)
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
	TokenEvent(t interface{})
}

AuthRequest has methods for responding to auth requests.

type CallHandler

type CallHandler func(CallRequest)

CallHandler is a function called on resource call requests

type CallRequest

type CallRequest interface {
	Resource
	Method() string
	CID() string
	RawParams() json.RawMessage
	RawToken() json.RawMessage
	ParseParams(interface{})
	ParseToken(interface{})
	OK(result interface{})
	Resource(rid string)
	NotFound()
	MethodNotFound()
	InvalidParams(message string)
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
}

CallRequest has methods for responding to call requests.

type CollectionHandler

type CollectionHandler func(CollectionRequest)

CollectionHandler is a function called on collection get requests

type CollectionRequest

type CollectionRequest interface {
	Resource
	Collection(collection interface{})
	QueryCollection(collection interface{}, query string)
	NotFound()
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
	ForValue() bool
}

CollectionRequest has methods for responding to collection get requests.

type Conn

type Conn interface {
	// Publish publishes the data argument to the given subject
	Publish(subject string, payload []byte) error

	// PublishRequest publishes a request expecting a response on the reply
	// subject.
	PublishRequest(subject, reply string, data []byte) error

	// ChanSubscribe subscribes to messages matching the subject pattern.
	ChanSubscribe(subject string, ch chan *nats.Msg) (*nats.Subscription, error)

	// ChanQueueSubscribe subscribes to messages matching the subject pattern.
	// All subscribers with the same queue name will form the queue group and
	// only one member of the group will be selected to receive any given
	// message, which will be placed on the channel.
	ChanQueueSubscribe(subject, queue string, ch chan *nats.Msg) (*nats.Subscription, error)

	// Close will close the connection to the server.
	Close()
}

Conn is an interface that represents a connection to a NATS server. It is implemented by nats.Conn.

type DataValue added in v0.4.0

type DataValue struct {
	Data interface{} `json:"data"`
}

DataValue is a wrapper for values that may marshal into any type of json value, including objects, arrays, or nested structures.

If a value marshals into a json object or array, it must be wrapped with DataValue or similar, or else the value will be considered invalid.

Example:

s.Handle("timezones", res.GetCollection(func(r res.CollectionRequest) {
	type tz struct {
		Abbr   string `json:"abbr"`
		Offset int    `json:"offset"`
	}
	r.Collection([]res.DataValue{
		res.DataValue{tz{"GMT", 0}},
		res.DataValue{tz{"CET", 1}},
	})
}))

For objects and arrays, it marshals into a data value object, eg.:

json.Marshal(res.DataValue{[]int{1, 2, 3}}) // Result: {"data":[1,2,3]}

For strings, numbers, booleans, and null values, it marshals into a primitive value, eg.:

json.Marshal(res.DataValue{nil}) // Result: null

type Error

type Error struct {
	Code    string      `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

Error represents an RES error

func InternalError

func InternalError(err error) *Error

InternalError converts an error to an *Error with the code system.internalError.

func ToError

func ToError(err error) *Error

ToError converts an error to an *Error. If it isn't of type *Error already, it will become a system.internalError.

func (*Error) Error

func (e *Error) Error() string

type Event added in v0.2.0

type Event struct {
	// Name of the event.
	Name string

	// Resource emitting the event.
	Resource Resource

	// New property values for the model emitting the event.
	// * Only valid for "change" events.
	NewValues map[string]interface{}

	// Old property values for the model emitting the event.
	// * Only valid for "change" events.
	// * Value will be Delete for new properties.
	OldValues map[string]interface{}

	// Value being added or removed from
	// the collection emitting the event.
	// * Only valid for "add" and "remove" events.
	// * Only set for "remove" events if an ApplyRemove handler is defined.
	Value interface{}

	// Index position where the value is added or removed from
	// the collection emitting the event.
	// * Only valid for "add" and "remove" events.
	Idx int

	// Data for the created or deleted resource.
	// * Only valid for "create" and "delete" events.
	// * Only set for "delete" events if an ApplyDelete handler is defined.
	Data interface{}

	// Payload of a custom event.
	Payload interface{}
}

Event represents an event emitted by resource.

type GetHandler

type GetHandler func(GetRequest)

GetHandler is a function called on untyped get requests

type GetRequest

type GetRequest interface {
	Resource
	Model(model interface{})
	QueryModel(model interface{}, query string)
	Collection(collection interface{})
	QueryCollection(collection interface{}, query string)
	NotFound()
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
	ForValue() bool
}

GetRequest has methods for responding to resource get requests.

type Handler

type Handler struct {
	// Resource type
	Type ResourceType

	// Access handler for access requests
	Access AccessHandler

	// Get handler for get requests.
	Get GetHandler

	// Call handlers for call requests
	Call map[string]CallHandler

	// New handler for new call requests
	//
	// Deprecated: Use Call with Resource response instead; deprecated in RES
	// protocol v1.2.0
	New NewHandler

	// Auth handler for auth requests
	Auth map[string]AuthHandler

	// ApplyChange handler for applying change event mutations
	ApplyChange ApplyChangeHandler

	// ApplyAdd handler for applying add event mutations
	ApplyAdd ApplyAddHandler

	// ApplyRemove handler for applying remove event mutations
	ApplyRemove ApplyRemoveHandler

	// ApplyCreate handler for applying create event
	ApplyCreate ApplyCreateHandler

	// ApplyDelete handler for applying delete event
	ApplyDelete ApplyDeleteHandler

	// Group is the identifier of the group the resource belongs to. All
	// resources of the same group will be handled on the same goroutine. The
	// group may contain tags, ${tagName}, where the tag name matches a
	// parameter placeholder name in the resource pattern. If empty, the
	// resource name will be used as identifier.
	Group string

	// OnRegister is callback that is to be call when the handler has been
	// registered to a service.
	//
	// The pattern is the full resource pattern for the resource, including any
	// service name or mount paths.
	//
	// The handler is the handler being registered, and should be considered
	// immutable.
	OnRegister func(service *Service, pattern Pattern, rh Handler)

	// Listeners is a map of event listeners, where the key is the resource
	// pattern being listened on, and the value being the callback called on
	// events.
	//
	// The callback will be called in the context of the resource emitting the
	// event.
	Listeners map[string]func(*Event)
}

Handler contains handler functions for a given resource pattern.

func (*Handler) Option added in v0.3.0

func (h *Handler) Option(hf ...Option)

Option sets handler fields by passing one or more handler options.

type Match added in v0.2.0

type Match struct {
	Handler   Handler
	Listeners []func(*Event)
	Params    map[string]string
	Group     string
}

Match is a handler matching a resource name.

type ModelHandler

type ModelHandler func(ModelRequest)

ModelHandler is a function called on model get requests

type ModelRequest

type ModelRequest interface {
	Resource
	Model(model interface{})
	QueryModel(model interface{}, query string)
	NotFound()
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
	ForValue() bool
}

ModelRequest has methods for responding to model get requests.

type Mux

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

Mux stores handlers and efficiently retrieves them for resource names matching a pattern.

func NewMux

func NewMux(path string) *Mux

NewMux returns a new root Mux starting with given resource name path. Use an empty path to not add any prefix to the resource names.

func (*Mux) AddHandler

func (m *Mux) AddHandler(pattern string, hs Handler)

AddHandler register a handler for the given resource pattern. The pattern used is the same as described for Handle.

func (*Mux) AddListener added in v0.2.0

func (m *Mux) AddListener(pattern string, handler func(*Event))

AddListener adds a listener for events that occurs on resources matching the exact pattern.

func (*Mux) Contains

func (m *Mux) Contains(test func(h Handler) bool) bool

Contains traverses through the registered handlers to see if any of them matches the predicate test.

func (*Mux) FullPath

func (m *Mux) FullPath() string

FullPath returns the path that prefix all resource handlers, including the pattern derived from being mounted.

func (*Mux) GetHandler

func (m *Mux) GetHandler(rname string) *Match

GetHandler parses the resource name and gets the registered handler, event listeners, path params, and group ID. Returns the matching handler, or nil if not found.

func (*Mux) Handle

func (m *Mux) Handle(pattern string, hf ...Option)

Handle registers the handler functions for the given resource pattern.

A pattern may contain placeholders that acts as wildcards, and will be parsed and stored in the request.PathParams map. A placeholder is a resource name part starting with a dollar ($) character:

s.Handle("user.$id", handler) // Will match "user.10", "user.foo", etc.

An anonymous placeholder is a resource name part using an asterisk (*) character:

s.Handle("user.*", handler)   // Will match "user.10", "user.foo", etc.

A full wildcard can be used as last part using a greather than (>) character:

s.Handle("data.>", handler)   // Will match "data.foo", "data.foo.bar", etc.

If the pattern is already registered, or if there are conflicts among the handlers, Handle panics.

func (*Mux) Mount

func (m *Mux) Mount(path string, sub *Mux)

Mount attaches another Mux at a given path. When mounting, any path set on the sub Mux will be suffixed to the path.

func (*Mux) Path

func (m *Mux) Path() string

Path returns the path that prefix all resource handlers, not including the pattern derived from being mounted.

func (*Mux) Register added in v0.2.0

func (m *Mux) Register(s *Service)

Register registers the mux to a service. Will panic if already registered, or mounted to another mux.

func (*Mux) Route

func (m *Mux) Route(subpath string, fn func(m *Mux)) *Mux

Route create a new Mux and mounts it to the given subpath.

func (*Mux) ValidateListeners added in v0.2.0

func (m *Mux) ValidateListeners() (err error)

ValidateListeners validates that all patterns with event listeners has registered handlers, or panics if a handler is missing.

type NewHandler deprecated

type NewHandler func(NewRequest)

NewHandler is a function called on new resource call requests

Deprecated: Use CallHandler with Resource response instead; deprecated in RES protocol v1.2.0

type NewRequest

type NewRequest interface {
	Resource
	CID() string
	RawParams() json.RawMessage
	RawToken() json.RawMessage
	ParseParams(interface{})
	ParseToken(interface{})
	New(rid Ref)
	NotFound()
	MethodNotFound()
	InvalidParams(message string)
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
}

NewRequest has methods for responding to new call requests.

type Option

type Option interface{ SetOption(*Handler) }

Option set one or more of the handler functions for a resource Handler.

func Access

func Access(h AccessHandler) Option

Access sets a handler for resource access requests.

func ApplyAdd

func ApplyAdd(h ApplyAddHandler) Option

ApplyAdd sets a handler for applying add events.

func ApplyChange

func ApplyChange(h ApplyChangeHandler) Option

ApplyChange sets a handler for applying change events.

func ApplyCreate

func ApplyCreate(h ApplyCreateHandler) Option

ApplyCreate sets a handler for applying create events.

func ApplyDelete

func ApplyDelete(h ApplyDeleteHandler) Option

ApplyDelete sets a handler for applying delete events.

func ApplyRemove

func ApplyRemove(h ApplyRemoveHandler) Option

ApplyRemove sets a handler for applying remove events.

func Auth

func Auth(method string, h AuthHandler) Option

Auth sets a handler for resource auth requests.

func Call

func Call(method string, h CallHandler) Option

Call sets a handler for resource call requests.

Panics if the method is the pre-defined call method set.

For pre-defined set call methods, the handler Set should be used instead.

func GetCollection

func GetCollection(h CollectionHandler) Option

GetCollection sets a handler for collection get requests.

func GetModel

func GetModel(h ModelHandler) Option

GetModel sets a handler for model get requests.

func GetResource

func GetResource(h GetHandler) Option

GetResource sets a handler for untyped resource get requests.

func Group

func Group(group string) Option

Group sets a group ID. All resources of the same group will be handled on the same goroutine.

The group may contain tags, ${tagName}, where the tag name matches a parameter placeholder name in the resource pattern.

func New deprecated

func New(h NewHandler) Option

New sets a handler for new resource requests.

Deprecated: Use Call with Resource response instead; deprecated in RES protocol v1.2.0

func OnRegister added in v0.2.0

func OnRegister(callback func(service *Service, pattern Pattern, rh Handler)) Option

OnRegister sets a callback to be called when the handler is registered to a service.

If a callback is already registered, the new callback will be called after the previous one.

func Set

func Set(h CallHandler) Option

Set sets a handler for set resource requests.

Is a n alias for Call("set", h)

type OptionFunc

type OptionFunc func(*Handler)

The OptionFunc type is an adapter to allow the use of ordinary functions as options. If f is a function with the appropriate signature, OptionFunc(f) is an Option that calls f.

func (OptionFunc) SetOption

func (f OptionFunc) SetOption(hs *Handler)

SetOption calls f(hs)

type Pattern added in v0.2.0

type Pattern string

Pattern is a resource pattern that may contain wildcards and tags.

Pattern("example.resource.>") // Full wild card (>) matches anything that follows
Pattern("example.item.*")     // Wild card (*) matches a single part
Pattern("example.model.$id")  // Tag (starting with $) matches a single part

func (Pattern) IndexWildcard added in v0.2.0

func (p Pattern) IndexWildcard() int

IndexWildcard returns the index of the first instance of a wild card (*, >, or $tag) in pattern, or -1 if no wildcard is present.

Behavior is undefined for an invalid pattern.

func (Pattern) IsValid added in v0.2.0

func (p Pattern) IsValid() bool

IsValid returns true if the pattern is valid, otherwise false.

func (Pattern) Matches added in v0.2.0

func (p Pattern) Matches(s string) bool

Matches tests if the resource name, s, matches the pattern.

The resource name might in itself contain wild cards and tags.

Behavior is undefined for an invalid pattern or an invalid resource name.

func (Pattern) ReplaceTag added in v0.3.0

func (p Pattern) ReplaceTag(tag string, value string) Pattern

ReplaceTag searches for a given tag (without $) and replaces it with the value.

Behavior is undefined for an invalid pattern.

func (Pattern) ReplaceTags added in v0.3.0

func (p Pattern) ReplaceTags(m map[string]string) Pattern

ReplaceTags searches for tags and replaces them with the map value for the key matching the tag (without $).

Behavior is undefined for an invalid pattern.

func (Pattern) Values added in v0.3.0

func (p Pattern) Values(s string) (map[string]string, bool)

Values extracts the tag values from a resource name, s, matching the pattern.

The returned bool flag is true if s matched the pattern, otherwise false with a nil map.

Behavior is undefined for an invalid pattern or an invalid resource name.

type QueryRequest

type QueryRequest interface {
	Resource
	Model(model interface{})
	Collection(collection interface{})
	NotFound()
	InvalidQuery(message string)
	Error(err error)
	Timeout(d time.Duration)
}

QueryRequest has methods for responding to query requests.

type Ref

type Ref string

Ref is a resource reference to another resource ID.

It marshals into a reference object, eg.:

{"rid":"userService.user.42"}

func (Ref) IsValid

func (r Ref) IsValid() bool

IsValid returns true if the reference RID is valid, otherwise false.

func (Ref) MarshalJSON

func (r Ref) MarshalJSON() ([]byte, error)

MarshalJSON makes Ref implement the json.Marshaler interface.

func (*Ref) UnmarshalJSON

func (r *Ref) UnmarshalJSON(b []byte) error

UnmarshalJSON makes Ref implement the json.Unmarshaler interface.

type Request

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

Request represent a RES request

func (*Request) Access

func (r *Request) Access(get bool, call string)

Access sends a successful response.

The get flag tells if the client has access to get (read) the resource. The call string is a comma separated list of methods that the client can call. Eg. "set,foo,bar". A single asterisk character ("*") means the client is allowed to call any method. Empty string means no calls are allowed.

Only valid for access requests.

func (*Request) AccessDenied

func (r *Request) AccessDenied()

AccessDenied sends a system.accessDenied response.

Only valid for access requests.

func (*Request) AccessGranted

func (r *Request) AccessGranted()

AccessGranted a successful response granting full access to the resource. Same as calling:

Access(true, "*");

Only valid for access requests.

func (*Request) AddEvent

func (r *Request) AddEvent(v interface{}, idx int)

AddEvent sends an add event, adding the value v at index idx.

Only valid for a collection resource.

func (*Request) CID

func (r *Request) CID() string

CID returns the connection ID of the requesting client connection. Empty string for get requests.

func (*Request) ChangeEvent

func (r *Request) ChangeEvent(changed map[string]interface{})

ChangeEvent sends a change event. The changed map's keys are the key names for the model properties, and the values are the new values. If changed is empty, no event is sent.

Only valid for a model resource.

func (*Request) Collection

func (r *Request) Collection(collection interface{})

Collection sends a successful collection response for the get request. The collection must marshal into a JSON array.

Only valid for get requests for a collection resource.

func (*Request) CreateEvent

func (r *Request) CreateEvent(data interface{})

CreateEvent sends a create event for the resource, where data is the created resource data.

func (*Request) DeleteEvent

func (r *Request) DeleteEvent()

DeleteEvent sends a delete event.

func (*Request) Error

func (r *Request) Error(err error)

Error sends a custom error response for the request.

func (*Request) Event

func (r *Request) Event(event string, payload interface{})

Event sends a custom event on the resource. Will panic if the event is one of the pre-defined or reserved events, "change", "delete", "add", "remove", "patch", "reaccess", "unsubscribe", or "query". For pre-defined events, the matching method, ChangeEvent, AddEvent, RemoveEvent, CreateEvent, DeleteEvent, or ReaccessEvent should be used instead.

This is to ensure compliance with the specifications: https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#events

func (*Request) ForValue

func (r *Request) ForValue() bool

ForValue is used to tell whether a get request handler is called as a result of Value being called from another handler.

Only valid for get requests.

func (*Request) Group

func (r *Request) Group() string

Group returns the group which the resource shares the worker goroutine with. Will be the resource name of no specific group was set.

func (*Request) Header

func (r *Request) Header() map[string][]string

Header returns the HTTP headers sent by client on connect.

Only set for auth requests.

func (*Request) Host

func (r *Request) Host() string

Host returns the host on which the URL is sought by the client. Per RFC 2616, this is either the value of the "Host" header or the host name given in the URL itself.

Only set for auth requests.

func (*Request) InvalidParams

func (r *Request) InvalidParams(message string)

InvalidParams sends a system.invalidParams response. An empty message will default to "Invalid parameters".

Only valid for call and auth requests.

func (*Request) InvalidQuery added in v0.2.0

func (r *Request) InvalidQuery(message string)

InvalidQuery sends a system.invalidQuery response. An empty message will default to "Invalid query".

func (*Request) Method

func (r *Request) Method() string

Method returns the resource method. Empty string for access and get requests.

func (*Request) MethodNotFound

func (r *Request) MethodNotFound()

MethodNotFound sends a system.methodNotFound response for the request.

Only valid for call and auth requests.

func (*Request) Model

func (r *Request) Model(model interface{})

Model sends a successful model response for the get request. The model must marshal into a JSON object.

Only valid for get requests for a model resource.

func (*Request) New deprecated

func (r *Request) New(rid Ref)

New sends a successful response for the new call request. Panics if rid is invalid.

Only valid for new call requests.

Deprecated: Use Resource method instead; deprecated in RES protocol v1.2.0

func (*Request) NotFound

func (r *Request) NotFound()

NotFound sends a system.notFound response for the request.

func (*Request) OK

func (r *Request) OK(result interface{})

OK sends a successful result response to a request. The result may be nil.

Only valid for call and auth requests.

func (*Request) ParseParams

func (r *Request) ParseParams(p interface{})

ParseParams unmarshals the JSON encoded parameters and stores the result in p. If the request has no parameters, ParseParams does nothing. On any error, ParseParams panics with a system.invalidParams *Error.

Only valid for call and auth requests.

func (*Request) ParseQuery

func (r *Request) ParseQuery() url.Values

ParseQuery parses the query and returns the corresponding values. It silently discards malformed value pairs. To check errors use url.ParseQuery.

func (*Request) ParseToken

func (r *Request) ParseToken(t interface{})

ParseToken unmarshals the JSON encoded token and stores the result in t. If the request has no token, ParseToken does nothing. On any error, ParseToken panics with a system.internalError *Error.

Not valid for get requests.

func (*Request) PathParam

func (r *Request) PathParam(key string) string

PathParam returns the parameter derived from the resource name for the key placeholder.

func (*Request) PathParams

func (r *Request) PathParams() map[string]string

PathParams returns parameters that are derived from the resource name.

func (*Request) Query

func (r *Request) Query() string

Query returns the query part of the resource ID without the question mark separator.

func (*Request) QueryCollection

func (r *Request) QueryCollection(collection interface{}, query string)

QueryCollection sends a successful query collection response for the get request. The collection must marshal into a JSON array.

Only valid for get requests for a collection query resource.

func (*Request) QueryEvent

func (r *Request) QueryEvent(cb func(QueryRequest))

QueryEvent sends a query event on the resource, calling the provided callback on any query request. The last call to the callback will always be with nil, indicating that the query event duration has expired.

func (*Request) QueryModel

func (r *Request) QueryModel(model interface{}, query string)

QueryModel sends a successful query model response for the get request. The model must marshal into a JSON object.

Only valid for get requests for a model query resource.

func (*Request) RawParams

func (r *Request) RawParams() json.RawMessage

RawParams returns the JSON encoded method parameters, or nil if the request had no parameters. Always returns nil for access and get requests.

func (*Request) RawToken

func (r *Request) RawToken() json.RawMessage

RawToken returns the JSON encoded access token, or nil if the request had no token. Always returns nil for get requests.

func (*Request) ReaccessEvent

func (r *Request) ReaccessEvent()

ReaccessEvent sends a reaccess event.

func (*Request) RemoteAddr

func (r *Request) RemoteAddr() string

RemoteAddr returns the network address of the client sent on connect. The format is not specified.

Only set for auth requests.

func (*Request) RemoveEvent

func (r *Request) RemoveEvent(idx int)

RemoveEvent sends an remove event, removing the value at index idx.

Only valid for a collection resource.

func (*Request) RequireValue

func (r *Request) RequireValue() interface{}

RequireValue uses Value to gets the resource value, provided from the Get resource handler. It panics if the underlying call to Value return an error.

func (*Request) ResetEvent added in v0.2.0

func (r *Request) ResetEvent()

ResetEvent sends a system.reset event for the specific resource.

func (*Request) Resource added in v0.2.0

func (r *Request) Resource(rid string)

Resource sends a successful resource response to a request. The rid string must be a valid resource ID.

Only valid for call and auth requests.

func (*Request) ResourceName

func (r *Request) ResourceName() string

ResourceName returns the resource name.

func (*Request) ResourceType

func (r *Request) ResourceType() ResourceType

ResourceType returns the resource type.

func (*Request) Service

func (r *Request) Service() *Service

Service returns the service instance

func (*Request) Timeout

func (r *Request) Timeout(d time.Duration)

Timeout attempts to set the timeout duration of the request. The call has no effect if the requester has already timed out the request.

func (*Request) TokenEvent

func (r *Request) TokenEvent(token interface{})

TokenEvent sends a connection token event that sets the requester's connection access token, discarding any previously set token. A change of token will invalidate any previous access response received using the old token. A nil token clears any previously set token. To set the connection token for a different connection ID, use Service.TokenEvent.

Only valid for auth requests.

func (*Request) Type

func (r *Request) Type() string

Type returns the request type. May be "access", "get", "call", or "auth".

func (*Request) URI

func (r *Request) URI() string

URI returns the unmodified Request-URI of the Request-Line (RFC 2616, Section 5.1) as sent by the client on connect.

Only set for auth requests.

func (*Request) Value

func (r *Request) Value() (interface{}, error)

Value gets the resource value as provided from the Get resource handlers. If it fails to get the resource value, or no get handler is defined, it returns a nil interface and a *Error type error.

type Resource

type Resource interface {
	// Service returns the service instance
	Service() *Service

	// Resource returns the resource name.
	ResourceName() string

	// ResourceType returns the resource type.
	ResourceType() ResourceType

	// PathParams returns parameters that are derived from the resource name.
	PathParams() map[string]string

	// PathParam returns the key placeholder parameter value derived from the resource name.
	PathParam(string) string

	// Query returns the query part of the resource ID without the question mark separator.
	Query() string

	// Group which the resource shares worker goroutine with.
	// Will be the resource name of no specific group was set.
	Group() string

	// ParseQuery parses the query and returns the corresponding values.
	// It silently discards malformed value pairs.
	// To check errors use url.ParseQuery(Query()).
	ParseQuery() url.Values

	// Value gets the resource value as provided from the Get resource handlers.
	// If it fails to get the resource value, or no get handler is
	// defined, it returns a nil interface and a *Error type error.
	Value() (interface{}, error)

	// RequireValue gets the resource value as provided from the Get resource handlers.
	// Panics if it fails to get the resource value, or no get handler is defined.
	RequireValue() interface{}

	// Event sends a custom event on the resource.
	// Will panic if the event is one of the pre-defined or reserved events,
	// "change", "delete", "add", "remove", "patch", "reaccess", "unsubscribe", or "query".
	// For pre-defined events, the matching method, ChangeEvent, AddEvent,
	// RemoveEvent, CreateEvent, DeleteEvent, or ReaccessEvent should be used instead.
	//
	// This is to ensure compliance with the specifications:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#events
	Event(event string, payload interface{})

	// ChangeEvents sends a change event with properties that has been changed
	// and their new values.
	// If props is empty, no event is sent.
	// Panics if the resource is not a Model.
	// The values must be serializable into JSON primitives, resource references,
	// or a delete action objects.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#model-change-event
	ChangeEvent(props map[string]interface{})

	// AddEvent sends an add event, adding the value at index idx.
	// Panics if the resource is not a Collection, or if idx is less than 0.
	// The value must be serializable into a JSON primitive or resource reference.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#collection-add-event
	AddEvent(value interface{}, idx int)

	// RemoveEvent sends a remove event, removing the value at index idx.
	// Panics if the resource is not a Collection, or if idx is less than 0.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#collection-remove-event
	RemoveEvent(idx int)

	// ReaccessEvent sends a reaccess event to signal that the resource's access permissions has changed.
	// It will invalidate any previous access response sent for the resource.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#reaccess-event
	ReaccessEvent()

	// ResetEvent sends a reset event to signal that the resource's data has changed.
	// It will invalidate any previous get response sent for the resource.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#reaccess-event
	ResetEvent()

	// QueryEvent sends a query event to signal that the query resource's underlying data has been modified.
	// See the protocol specification for more information:
	//    https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#query-event
	QueryEvent(func(QueryRequest))

	// CreateEvent sends a create event, to signal the resource has been created, with
	// value being the resource value.
	CreateEvent(value interface{})

	// DeleteEvent sends a delete event, to signal the resource has been deleted.
	DeleteEvent()
}

Resource represents a resource

type ResourceType

type ResourceType byte

ResourceType enum

const (
	TypeUnset ResourceType = iota
	TypeModel
	TypeCollection
)

Resource type enum values

type Service

type Service struct {
	*Mux
	// contains filtered or unexported fields
}

A Service handles incoming requests from NATS Server and calls the appropriate callback on the resource handlers.

func NewService

func NewService(name string) *Service

NewService creates a new Service.

The name is the service name which will be prefixed to all resources. It must be an alphanumeric string with no embedded whitespace, or empty. If name is an empty string, the Service will by default handle all resources for all namespaces. Use SetReset to limit the namespace scope.

func (*Service) Conn added in v0.4.0

func (s *Service) Conn() Conn

Conn returns the connection instance used by the service.

If the service is not started, nil is returned.

If the service was started using ListenAndServe, the connection will be of type *nats.Conn:

nc := service.Conn().(*nats.Conn)

func (*Service) ListenAndServe

func (s *Service) ListenAndServe(url string, options ...nats.Option) error

ListenAndServe connects to the NATS server at the url. Once connected, it subscribes to incoming requests. For each request, it calls the appropriate handler, or replies with the appropriate error if no handler is available.

In case of disconnect, it will try to reconnect until Close is called, or until successfully reconnecting, upon which Reset will be called.

ListenAndServe returns an error if failes to connect or subscribe. Otherwise, nil is returned once the connection is closed using Close.

func (*Service) Logger

func (s *Service) Logger() logger.Logger

Logger returns the logger.

func (*Service) ProtocolVersion added in v0.2.0

func (s *Service) ProtocolVersion() string

ProtocolVersion returns the supported RES protocol version.

func (*Service) Reset

func (s *Service) Reset(resources []string, access []string)

Reset sends a system reset for the provided resource patterns.

func (*Service) ResetAll

func (s *Service) ResetAll()

ResetAll will send a system.reset to trigger any gateway to update their cache for all resources handled by the service.

The method is automatically called on server start and reconnects.

func (*Service) Resource

func (s *Service) Resource(rid string) (Resource, error)

Resource matches the resource ID, rid, with the registered Handlers and returns the resource, or an error if there is no matching handler found.

Should only be called from within the resource's group goroutine. Using the returned value from another goroutine may cause race conditions.

func (*Service) Serve

func (s *Service) Serve(conn Conn) error

Serve starts serving incoming requests received on the connection conn. For each request, it calls the appropriate handler, or replies with the appropriate error if no handler is available.

If the connection conn is of type *nats.Conn, Service will call SetReconnectHandler, SetDisconnectHandler, and SetClosedHandler, replacing any existing event handlers.

In case of disconnect, it will try to reconnect until Close is called, or until successfully reconnecting, upon which Reset will be called.

Serve returns an error if failes to subscribe. Otherwise, nil is returned once the connection is closed.

func (*Service) SetInChannelSize added in v0.4.7

func (s *Service) SetInChannelSize(size int) *Service

SetInChannelSize sets the size of the in channel receiving messages from NATS Server. Default is 1024.

If size is less or equal to zero, the default value is used.

func (*Service) SetLogger

func (s *Service) SetLogger(l logger.Logger) *Service

SetLogger sets the logger. Panics if service is already started.

func (*Service) SetOnDisconnect

func (s *Service) SetOnDisconnect(f func(*Service))

SetOnDisconnect sets a function to call when the service has been disconnected from NATS server.

func (*Service) SetOnError

func (s *Service) SetOnError(f func(*Service, string))

SetOnError sets a function to call on errors within the service, or incoming messages not complying with the RES protocol.

func (*Service) SetOnReconnect

func (s *Service) SetOnReconnect(f func(*Service))

SetOnReconnect sets a function to call when the service has reconnected to NATS server and sent a system reset event.

func (*Service) SetOnServe

func (s *Service) SetOnServe(f func(*Service))

SetOnServe sets a function to call when the service has started after sending the initial system reset event.

func (*Service) SetOwnedResources

func (s *Service) SetOwnedResources(resources, access []string) *Service

SetOwnedResources sets the patterns which the service will handle requests for. The resources slice patterns ill be listened to for get, call, and auth requests. The access slice patterns will be listened to for access requests. These patterns will be used when a ResetAll is made.

// Handle all requests for resources prefixed "library."
service.SetOwnedResources([]string{"library.>"}, []string{"library.>"})
// Handle access requests for any resource
service.SetOwnedResources([]string{}, []string{">"})
// Handle non-access requests for a subset of resources
service.SetOwnedResources([]string{"library.book", "library.books.*"}, []string{})

If set to nil (default), the service will default to set ownership of all resources prefixed with its own path if one was provided when creating the service (eg. "serviceName.>"), or to all resources if no name was provided. It will take resource ownership if it has at least one registered handler has a Get, Call, or Auth handler method not being nil. It will take access ownership if it has at least one registered handler with the Access method not being nil.

For more details on system reset, see: https://github.com/resgateio/resgate/blob/master/docs/res-service-protocol.md#system-reset-event

func (*Service) SetQueryEventDuration

func (s *Service) SetQueryEventDuration(d time.Duration) *Service

SetQueryEventDuration sets the duration for which the service will listen for query requests sent on a query event. Default is 3 seconds

func (*Service) SetQueueGroup added in v0.4.0

func (s *Service) SetQueueGroup(queue string) *Service

SetQueueGroup sets the queue group to use when subscribing to resources. By default it will be the same as the service name.

If queue is set to an empty string, the service will not belong to any queue group.

func (*Service) SetReset deprecated

func (s *Service) SetReset(resources, access []string) *Service

SetReset is an alias for SetOwnedResources.

Deprecated: Renamed to SetOwnedResources to match API of similar libraries.

func (*Service) SetWorkerCount added in v0.4.7

func (s *Service) SetWorkerCount(count int) *Service

SetWorkerCount sets the number of workers handling incoming requests. Default is 32 workers.

If count is less or equal to zero, the default value is used.

func (*Service) Shutdown

func (s *Service) Shutdown() error

Shutdown closes any existing connection to NATS Server. Returns an error if service is not started.

func (*Service) TokenEvent

func (s *Service) TokenEvent(cid string, token interface{})

TokenEvent sends a connection token event that sets the connection's access token, discarding any previously set token.

A change of token will invalidate any previous access response received using the old token.

A nil token clears any previously set token.

func (*Service) TokenEventWithID added in v0.4.5

func (s *Service) TokenEventWithID(cid string, tokenID string, token interface{})

TokenEventWithID sends a connection token event in the same way as TokenEvent, but includes a token ID (tid).

The token ID is a string that identifies the token, used when calling TokenReset to update or clear a token.

func (*Service) TokenReset added in v0.4.5

func (s *Service) TokenReset(subject string, tokenID ...string)

TokenReset sends a token reset event for the provided token IDs.

The subject string is a message subject that will receive auth requests for any connections with a token matching any of the token IDs.

func (*Service) With

func (s *Service) With(rid string, cb func(r Resource)) error

With matches the resource ID, rid, with the registered Handlers before calling the callback, cb, on the worker goroutine for the resource name or group.

With will return an error and not call the callback if there is no matching handler found.

func (*Service) WithGroup

func (s *Service) WithGroup(group string, cb func(s *Service))

WithGroup calls the callback, cb, on the group's worker goroutine.

func (*Service) WithResource

func (s *Service) WithResource(r Resource, cb func())

WithResource enqueues the callback, cb, to be called by the resource's worker goroutine. If the resource belongs to a group, it will be called on the group's worker goroutine.

type SoftRef added in v0.4.0

type SoftRef string

SoftRef is a soft resource reference to another resource ID which will not automatically be followed by Resgate.

It marshals into a soft reference object, eg.:

{"rid":"userService.user.42","soft":true}

func (SoftRef) IsValid added in v0.4.0

func (r SoftRef) IsValid() bool

IsValid returns true if the soft reference RID is valid, otherwise false.

func (SoftRef) MarshalJSON added in v0.4.0

func (r SoftRef) MarshalJSON() ([]byte, error)

MarshalJSON makes SoftRef implement the json.Marshaler interface.

func (*SoftRef) UnmarshalJSON added in v0.4.0

func (r *SoftRef) UnmarshalJSON(b []byte) error

UnmarshalJSON makes SoftRef implement the json.Unmarshaler interface.

Directories

Path Synopsis
examples
02-edit-text
This is an example of a simple text field that can be edited by multiple clients.
This is an example of a simple text field that can be edited by multiple clients.
03-book-collection
This is an example RES service that shows a lists of books, where book titles can be added, edited and deleted by multiple users simultaneously.
This is an example RES service that shows a lists of books, where book titles can be added, edited and deleted by multiple users simultaneously.
04-book-collection-store
This is the Book Collection example where all changes are persisted using a badgerDB store.
This is the Book Collection example where all changes are persisted using a badgerDB store.
05-search-query
A customer management system, where you can search and filter customers by name and country.
A customer management system, where you can search and filter customers by name and country.
Package middleware provides middleware for the res package:
Package middleware provides middleware for the res package:
Package resprot provides low level structs and methods for communicating with services using the RES Service Protocol over NATS server.
Package resprot provides low level structs and methods for communicating with services using the RES Service Protocol over NATS server.
Package restest provides utilities for testing res services:
Package restest provides utilities for testing res services:

Jump to

Keyboard shortcuts

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