Documentation ¶
Index ¶
- Variables
- func GetDefaultMethodName(system string, method string, version uint64) string
- func IPAddress(r *http.Request) string
- func NewNoOpLogger() *slog.Logger
- func RequireLogger(ctx *Context) *slog.Logger
- func SplitMethodName(method string) (string, uint64)
- func ToKebabCase(input string) string
- func ToPascalCase(input string) string
- type AESSecret
- type AuthClient
- type AuthProvider
- type Context
- func (c *Context) CallMethod(method string, rpcHttpMethod RpcHttpMethod, payload any, bindata []byte) (any, error)
- func (c *Context) Deadline() (time.Time, bool)
- func (c *Context) Done() <-chan struct{}
- func (c *Context) Err() error
- func (c *Context) Finalize(err error) error
- func (c *Context) Fork() *Context
- func (c *Context) GetValue(inst reflect.Type) (any, error)
- func (c *Context) Invalidate(rt ...reflect.Type)
- func (c *Context) Require(inst reflect.Type) any
- func (c *Context) StoreValue(rt reflect.Type, val any)
- func (c *Context) Value(key any) any
- type DebugSecret
- type Error
- type ErrorData
- type Factory
- type Finalizeable
- type Handler
- type HttpGet
- type HttpMethodHandler
- type HttpPost
- type HttpRegexpHandler
- type HttpRequest
- type HttpResponseWriter
- type HttpRpcHandler
- type Impersonated
- type Impersonator
- type ImpersonatorProvider
- type MethodDefinition
- type MethodHandler
- func (m *MethodHandler) CallMethod(_ctx *Context, method string, rpcHttpMethod RpcHttpMethod, payload any, ...) (any, error)
- func (m *MethodHandler) GetSystem(sys any) any
- func (m *MethodHandler) RegisterMethod(def *MethodDefinition)
- func (m *MethodHandler) RegisterSystem(sys any, routeDebugger ...func(s string))
- type MethodHandlerOptions
- type MissingValidationLevel
- type Params
- type Private
- type Public
- type RealTime
- type RpcErrorResponse
- type RpcHttpMethod
- type RpcMeta
- type RpcNotification
- type RpcRequest
- type RpcResponseHeader
- type RpcResultResponse
- type RpcSource
- type Secret
- type Server
- type Shareable
- type ShareableAcrossImpersonation
- type Time
- type TimeProvider
- type ValidatedParams
- type Validator
- type WSClient
- type WebsocketHandler
- type WebsocketOptions
Constants ¶
This section is empty.
Variables ¶
var ( ErrParse = &Error{Code: -32700, Message: "Parse error"} ErrMethodNotFound = &Error{Code: -32601, Message: "Method not found"} ErrInvalidParams = &Error{Code: -32602, Message: "Invalid params"} ErrInternal = &Error{Code: -32603, Message: "Internal error"} ErrServerMethodNotAllowed = &Error{Code: -32000, Message: "Server error: method not allowed"} ErrUnauthenticated = &Error{Code: -32002, Message: "Not authenticated"} )
Rpc internal errors
var TypeContext = reflect.TypeOf((**Context)(nil)).Elem()
var TypeHttpRequest = reflect.TypeOf((**HttpRequest)(nil)).Elem()
var TypeHttpResponseWriter = reflect.TypeOf((**HttpResponseWriter)(nil)).Elem()
var TypeImpersonated = reflect.TypeOf((**Impersonated)(nil)).Elem()
var TypeImpersonator = reflect.TypeOf((**Impersonator)(nil)).Elem()
var TypePrivate = reflect.TypeOf((**Private)(nil)).Elem()
var TypePublic = reflect.TypeOf((**Public)(nil)).Elem()
var TypeRpcMeta = reflect.TypeOf((**RpcMeta)(nil)).Elem()
var TypeSecret = reflect.TypeOf((*Secret)(nil)).Elem()
var TypeTime = reflect.TypeOf((*Time)(nil)).Elem()
var TypeWSClient = reflect.TypeOf((**WSClient)(nil)).Elem()
Functions ¶
func GetDefaultMethodName ¶
func NewNoOpLogger ¶
func RequireLogger ¶
RequireLogger allows you to require the logger provided during initialization. In case no logger was provided, a NoOpLogger will be returned. The logger will be available by default.
func SplitMethodName ¶
func ToKebabCase ¶
ToKebabCase converts the provided string to kebab-case
func ToPascalCase ¶
ToPascalCase converts the provided string to PascalCase
Types ¶
type AESSecret ¶
type AESSecret struct { // contains filtered or unexported fields }
func NewAESSecret ¶
type AuthClient ¶
type AuthClient interface { // IsAuthenticated: does the caller possess a valid session - hence do we know who them is? // In case an error occurs (networking issues or others), IsAuthorized should return (nil, err); // In case of a missing authentication, the function should return (nil, nil) // In case of a valid authentication, the function should return (account's uuid, nil) IsAuthenticated(ctx *Context) (*string, error) // IsAuthorized: does the caller possess a valid session _and_ cann the caller access the current method? // In case an error occurs (networking issues or others), IsAuthorized should return (nil, err); // In case of a missing authorization, the function should return (nil, nil) // In case of a valid authorization, the function should return (account's uuid, nil) IsAuthorized(ctx *Context) (*string, error) }
AuthClient can be implemented by any backend which can check for IsAuthenticated or IsAuthorized.
type AuthProvider ¶
type AuthProvider struct {
// contains filtered or unexported fields
}
AuthProvider allows us to enable authentication within our calls.
func NewAuthProvider ¶
func NewAuthProvider( client AuthClient, ) *AuthProvider
NewAuthProvider returns a new instance of an auth provider
func (*AuthProvider) NewPrivate ¶
func (p *AuthProvider) NewPrivate(ctx *Context) *Private
NewPrivate returns a new private instance
func (*AuthProvider) NewPublic ¶
func (p *AuthProvider) NewPublic(ctx *Context) *Public
NewPublic returns a new public instance
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
func NewContext ¶
func NewContext(parent context.Context, factory *Factory, methodHandler *MethodHandler) *Context
func (*Context) CallMethod ¶
func (*Context) GetValue ¶
GetValue returns a value that's been previously required; In case the value does _not_ exist, an error will be returned; This method is usually not needed but can be necessary in case you're implementing new providers in the need to access previously initialized values _without_ explicitly initializing one (calling Require) checking for existance.
func (*Context) Invalidate ¶
type DebugSecret ¶
type DebugSecret struct {}
DebugSecret implements the Secret interface without actually encrypting the passed data: the data will be returned as-is. The functionality can be helpful for debugging certain error messages during development.
func NewDebugSecret ¶
func NewDebugSecret() *DebugSecret
func (*DebugSecret) Encode ¶
func (e *DebugSecret) Encode(in string) string
Encode may be used to embed sensitive information
type Error ¶
type Error struct { Code int `json:"code"` Message string `json:"message"` Data *ErrorData `json:"data,omitempty"` }
Error object
func Validate ¶
func Validate(secret Secret, validateable ValidatedParams, basePath ...string) *Error
Validate validates the handled interface
func (Error) CloneWithData ¶
CloneWithData returns a copy with the supplied data set
type ErrorData ¶
type ErrorData struct { Path []string `json:"path,omitempty"` Details []*Error `json:"details,omitempty"` Debug string `json:"debug,omitempty"` }
ErrorData object
type Factory ¶
type Factory struct {
// contains filtered or unexported fields
}
func NewFactory ¶
func (*Factory) RegisterProvider ¶
RegisterProvider registers a new Provider and panics on error The provider needs to be a pointer to a struct which provides methods accepting *jonson.Context and returning a single type. The method's name needs to be equal to the returned type's name and start with New. Example:
type Provider struct {} type Time struct {} func (p *Provider) NewTime() *Time{ return &Time{} } type DB struct {} func(p *Provider) NewDB() *DB { return &DB{} } fac := jonson.NewFactory() fac.RegisterProvider(&Provider{})
func (*Factory) RegisterProviderFunc ¶
RegisterProviderFunc allows us to register a single function returning a provider. Example:
func ProvideDB(ctx *jonson.Context)*sql.DB{ return &sql.NewDB() } fac := jonson.NewFactory() fac.RegisterProviderFunc(ProvideDB)
type Finalizeable ¶
type Handler ¶
type Handler interface {
Handle(w http.ResponseWriter, req *http.Request) bool
}
A handler will be handled by the server. You can decide which handler you feel like mounting, such as default http handlers, websocket handlers, an ajax rpc endpoint or exposing each rpc method as its own http endpoint.
type HttpGet ¶
type HttpGet interface {
// contains filtered or unexported methods
}
HttpGet can be used in case you want to enforce GET within your remote procedures served over http. In case you're using websockets or http rpc, GET cannot be enforced. For rpc over http (single endpoint), POST will be enforced by default. Example: func (s *System) GetProfileV1(ctx *jonson.Context, _ jonson.HttpGet) error{}
type HttpMethodHandler ¶
type HttpMethodHandler struct {
// contains filtered or unexported fields
}
HttpMethodHandler will register all methods within the methodHandler as separate http endpoints, such as: system/method.v1, system/another-method.v1 in case the method accepts params, POST is enforced, otherwise GET will be used as the accepting http method.
func NewHttpMethodHandler ¶
func NewHttpMethodHandler(methodHandler *MethodHandler) *HttpMethodHandler
func (*HttpMethodHandler) Handle ¶
func (h *HttpMethodHandler) Handle(w http.ResponseWriter, req *http.Request) bool
Handle handles the incoming http request and parses the payload. Since we do not need the json rpc wrapper for these calls (method is the http path), we only expect a _single_ data json object inside the body. in case
type HttpPost ¶
type HttpPost interface {
// contains filtered or unexported methods
}
HttpPost can be used in case you want to enforce POST within your remote procedures served over http. In case you're using websockets or http rpc, POST cannot be enforced. For rpc over http (single endpoint), POST will be enforced by default. func (s *System) UpdateProfileV1(ctx *jonson.Context, _ jonson.HttpPost) error{}
type HttpRegexpHandler ¶
type HttpRegexpHandler struct {
// contains filtered or unexported fields
}
The HttpRegexpHandler will accept regular expressions and will register those as default http endpoints. Those methods cannot be called within the rpc world
func NewHttpRegexpHandler ¶
func NewHttpRegexpHandler(factory *Factory, methodHandler *MethodHandler) *HttpRegexpHandler
func (*HttpRegexpHandler) Handle ¶
func (h *HttpRegexpHandler) Handle(w http.ResponseWriter, req *http.Request) bool
Handle will handle an incoming http request
func (*HttpRegexpHandler) RegisterRegexp ¶
func (h *HttpRegexpHandler) RegisterRegexp(pattern *regexp.Regexp, handler func(ctx *Context, w http.ResponseWriter, r *http.Request, parts []string))
RegisterRegexp registers a direct http func for a given regexp
type HttpRequest ¶
func RequireHttpRequest ¶
func RequireHttpRequest(ctx *Context) *HttpRequest
RequireHttpRequest returns the current http request. In case the connection was created using websockets, the underlying http.Request opening the connection will be returned.
type HttpResponseWriter ¶
type HttpResponseWriter struct { http.ResponseWriter }
func RequireHttpResponseWriter ¶
func RequireHttpResponseWriter(ctx *Context) *HttpResponseWriter
RequireHttpResponseWriter returns the current http response writer which is used to handle the ongoing request's response.
type HttpRpcHandler ¶
type HttpRpcHandler struct {
// contains filtered or unexported fields
}
func NewHttpRpcHandler ¶
func NewHttpRpcHandler(methodHandler *MethodHandler, path string) *HttpRpcHandler
func (*HttpRpcHandler) Handle ¶
func (h *HttpRpcHandler) Handle(w http.ResponseWriter, req *http.Request) bool
Handle will handle an incoming http request
type Impersonated ¶
type Impersonated struct { // across calls done within a scope of an impersonation Shareable // contains filtered or unexported fields }
Impersonated will be stored once an account has been impersonated. Use RequireImpersonated to gain access to the impersonated account.
func RequireImpersonated ¶
func RequireImpersonated(ctx *Context) *Impersonated
RequireImpersonated returns the current impersonated setting; In case no impersonation has been set, RequireImpersonated will panic
func RequireOptionalImpersonated ¶
func RequireOptionalImpersonated(ctx *Context) *Impersonated
RequireOptionalImpersonated returns impersonated in case it exists; In case no impersonation is taking place, RequireOptionalImpersonated will return nil
func (*Impersonated) AccountUuid ¶
func (i *Impersonated) AccountUuid() string
func (*Impersonated) TracedAccountUuids ¶
func (i *Impersonated) TracedAccountUuids() []string
TracedAccountUuids returns _all_ impersonated account uuids of the current scope: in case Account Alice impersonates Account Bob who impersonates Account Charly, TracedAccountUuids() will return [BobUuid, CharlyUuid]
type Impersonator ¶
type Impersonator struct {
// contains filtered or unexported fields
}
func RequireImpersonator ¶
func RequireImpersonator(ctx *Context) *Impersonator
RequireImpersonator returns the impersonator
func (*Impersonator) Impersonate ¶
func (i *Impersonator) Impersonate(accountUuid string, fn func(ctx *Context) error) error
Impersonate will impersonate an account. Once the impersonation happened, a new context will be created and the context will be in the scope of the impersonated account.
type ImpersonatorProvider ¶
type ImpersonatorProvider struct { }
ImpersonatorProvider allows us to provide impersonation functionality to jonson. Impersonation can be used to make calls towards the API on behalf of another user.
func NewImpersonatorProvider ¶
func NewImpersonatorProvider() *ImpersonatorProvider
func (*ImpersonatorProvider) NewImpersonator ¶
func (i *ImpersonatorProvider) NewImpersonator(ctx *Context) *Impersonator
NewImpersonator instantiates a new impersonator instance
type MethodDefinition ¶
type MethodDefinition struct { System string Method string Version uint64 HandlerFunc any // contains filtered or unexported fields }
MethodDefinition is used by MustRegisterAPI
type MethodHandler ¶
type MethodHandler struct {
// contains filtered or unexported fields
}
func NewMethodHandler ¶
func NewMethodHandler( factory *Factory, errorEncoder Secret, opts *MethodHandlerOptions, ) *MethodHandler
func (*MethodHandler) CallMethod ¶
func (m *MethodHandler) CallMethod(_ctx *Context, method string, rpcHttpMethod RpcHttpMethod, payload any, bindata []byte) (any, error)
func (*MethodHandler) GetSystem ¶
func (m *MethodHandler) GetSystem(sys any) any
GetSystem returns a system. The function will panic in case system does not exist
func (*MethodHandler) RegisterMethod ¶
func (m *MethodHandler) RegisterMethod(def *MethodDefinition)
RegisterMethod registers a new method
func (*MethodHandler) RegisterSystem ¶
func (m *MethodHandler) RegisterSystem(sys any, routeDebugger ...func(s string))
RegisterSystem registers an entire system using reflect based method lookups
type MethodHandlerOptions ¶
type MethodHandlerOptions struct {
MissingValidationLevel MissingValidationLevel
}
type MissingValidationLevel ¶
type MissingValidationLevel string
MissingValidationLevel allows us to set a validation level within the method handlers to enforce parameter validation or ignore validation if not present
const ( MissingValidationLevelIgnore MissingValidationLevel = "ignore" MissingValidationLevelInfo MissingValidationLevel = "info" MissingValidationLevelWarn MissingValidationLevel = "warn" MissingValidationLevelError MissingValidationLevel = "error" MissingValidationLevelFatal MissingValidationLevel = "fatal" )
type Params ¶
type Params struct { }
Params must be embedded as first element in all value containers
type Private ¶
type Private struct {
// contains filtered or unexported fields
}
Private references endpoints which are private NOTE: The private provider must never be shared across contexts: Assume requestor calling method /user/set.v1 which calls /user/get.v1 internally; In case the requestor does have permission to call /user/set.v1 but not /user/get.v1, we need to make sure to re-evaluate the authorization permissions - hence, recreate a Private instance upon in-memory method calls. This is being ensured by calling context.CallMethod which will internally fork a context and only copy those values to the new forked context explicitly marked as shareable. Therefore: never make Private shareable to keep the authorization working as expected
func RequirePrivate ¶
func (*Private) AccountUuid ¶
type Public ¶
type Public struct { // in case the requestor calls /user/get.v1 which is public and // then requests /user/get-image.v1 which is public will // still resolve to the same requestor. We can // safe ourselves a round trip to the authenticator and // pass the initial resolved value to the forked context Shareable // contains filtered or unexported fields }
Public references endpoints which are public
func (*Public) AccountUuid ¶
AccountUuid gets the underlying account uuid. The call towards AccountUuid is protected with a mutex: in case two callers try to access the account uuid at the same time, only one will do the request; Possible return values: nil, err --> the client had an error nil, nil --> no error, not authenticated account uuid, nil -> no error, authenticated
type RealTime ¶
type RealTime struct {}
RealTime implements time
func NewRealTime ¶
func NewRealTime() *RealTime
NewTime returns a time instance which provides us with real time information. You will probably use this time instance for your production build.
type RpcErrorResponse ¶
type RpcErrorResponse struct { RpcResponseHeader Error *Error `json:"error"` }
RpcErrorResponse object
func NewRpcErrorResponse ¶
func NewRpcErrorResponse(id json.RawMessage, e *Error) *RpcErrorResponse
NewRpcErrorResponse returns a new ErrorResponse
type RpcHttpMethod ¶
type RpcHttpMethod string
const ( RpcHttpMethodGet RpcHttpMethod = "GET" RpcHttpMethodPost RpcHttpMethod = "POST" RpcHttpMethodUnknown RpcHttpMethod = "UNKNOWN" )
type RpcMeta ¶
type RpcMeta struct { Method string HttpMethod RpcHttpMethod Source RpcSource }
RpcMeta contains Rpc call meta data information that has been set whenever a call towards an Rpc method happened
func RequireRpcMeta ¶
type RpcNotification ¶
type RpcNotification struct { Version string `json:"jsonrpc"` Method string `json:"method"` Params json.RawMessage `json:"params,omitempty"` }
RpcNotification object
func NewRpcNotification ¶
func NewRpcNotification(method string, payload any) *RpcNotification
type RpcRequest ¶
type RpcRequest struct { Version string `json:"jsonrpc"` ID json.RawMessage `json:"id"` Method string `json:"method"` Params json.RawMessage `json:"params"` }
RpcRequest object
func (*RpcRequest) UnmarshalAndValidate ¶
func (r *RpcRequest) UnmarshalAndValidate(errEncoder Secret, out any, bindata []byte) error
UnmarshalAndValidate fills the given interface with the supplied params
type RpcResponseHeader ¶
type RpcResponseHeader struct { Version string `json:"jsonrpc"` ID json.RawMessage `json:"id"` }
RpcResponseHeader object
func NewRpcResponseHeader ¶
func NewRpcResponseHeader(id json.RawMessage) RpcResponseHeader
NewRpcResponseHeader returns a new ResponseHeader
type RpcResultResponse ¶
type RpcResultResponse struct { RpcResponseHeader Result any `json:"result"` }
RpcResultResponse object
func NewRpcResultResponse ¶
func NewRpcResultResponse(id json.RawMessage, result any) *RpcResultResponse
NewRpcResultResponse returns a new ResultResponse
type Secret ¶
type Secret interface { Shareable ShareableAcrossImpersonation Encode(in string) string Decode(in string) (string, error) }
func RequireSecret ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server ...
func NewServer ¶
NewServer returns a new Server. The handlers provided to the server will be handled through iteration of provided handlers: the first handler returning "true" will stop the iteration through the handlers and the request will be seen as served.
func (*Server) ListenAndServe ¶
ListenAndServe will start listening on http on the given addr
type Shareable ¶
type Shareable interface {
// contains filtered or unexported methods
}
Shareable defines objects that can be shared between contexts and will be passed to new contexts created within existing contexts. In case you do have a provided method that needs to be forwarded to new contexts created in the current scope, mark them as Shareable:
type Time struct { jonson.Shareable time.Time }
type ShareableAcrossImpersonation ¶
type ShareableAcrossImpersonation interface {
// contains filtered or unexported methods
}
ShareableAcrossImpersonation marks provided entities as shareable across the impersonation context.
type Time ¶
Time is the interface that can be used within your application. You can mock this interface within your tests.
func RequireTime ¶
RequireHttpResponseWriter returns the current http response writer which is used to handle the ongoing request's response.
type TimeProvider ¶
type TimeProvider struct {
// contains filtered or unexported fields
}
TimeProvider allows us to provide a time within our application. In order to allow for mocking times, we can use this pre-defined provider to allow for specifying a pre-defined time in our tests which will allow us to move forward/backwards (if needed)
func NewTimeProvider ¶
func NewTimeProvider(inst ...func() Time) *TimeProvider
NewTimeProvider returns a new TimeProvider
func (*TimeProvider) NewTime ¶
func (t *TimeProvider) NewTime(ctx *Context) Time
type ValidatedParams ¶
type ValidatedParams interface {
JonsonValidate(validator *Validator)
}
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
func NewValidator ¶
type WSClient ¶
type WSClient struct { // contains filtered or unexported fields }
func NewWSClient ¶
func NewWSClient(ws *WebsocketHandler, methodHandler *MethodHandler, conn *websocket.Conn, r *http.Request) *WSClient
func RequireWSClient ¶
func (*WSClient) IPAddress ¶
IPAddress returns the underlying ip address which has been used when opening websocket connection
func (*WSClient) SendNotification ¶
func (w *WSClient) SendNotification(msg *RpcNotification) (err error)
type WebsocketHandler ¶
type WebsocketHandler struct {
// contains filtered or unexported fields
}
The websocket handler allows us to provide websocket functionality to the server.
func NewWebsocketHandler ¶
func NewWebsocketHandler( methodHandler *MethodHandler, path string, options *WebsocketOptions, ) *WebsocketHandler
func (*WebsocketHandler) Handle ¶
func (wb *WebsocketHandler) Handle(w http.ResponseWriter, req *http.Request) bool
Handle will compare the defined path within the websocket handler to the requested url path. In case paths match, a new websocket client will be registered.
type WebsocketOptions ¶
type WebsocketOptions struct { Upgrader *websocket.Upgrader MaxMessageSize int64 PingPeriod time.Duration PongWait time.Duration WriteWait time.Duration }
func NewWebsocketOptions ¶
func NewWebsocketOptions() *WebsocketOptions