rony

package module
v0.12.7 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2024 License: BSD-3-Clause Imports: 11 Imported by: 4

README

Rony

Rony is a framework for developing high-performance API servers. It is designed to be simple and flexible. It uses the power of Go generics to provide an easy and robust code and helps you to detect a good few of the common mistakes at compile time. If you need to have more control over your code, such as selecting your own Gateway and Cluster, you can use Kit Package however for most use cases we recommend to use rony package instead.

Installation

To install Rony package, you need to install Go and set your Go workspace first.

First need to installGo (version 1.19+ is required), then you can use the below Go command to install RonyKIT.

$ go get -u github.com/clubpay/ronykit/rony/...

Quick start

State

When you develop API servers, you usually have a common state which could be shared between a few of your endpoints (i.e., Contracts). For example, you may have a database connection or a cache connection which you want to share between your endpoints. Moreover, you may want to have a shared state like a counter of the requests you received or in a simple chat application to keep the list of the connected users. Rony lets you define your own state and provides it to your handlers, so you can access it from your handlers without relying on global variables or defining your handler functions as methods of a common structs. The later two approaches can be problematic as your project grows.

The following code shows the type parameter of the State that your need to implement for your server.

package rony

// State related types
type (
	Action          comparable
	State[A Action] interface {
		Name() string
		Reduce(action A)
	}
)

As you can see, the State is a generic type which has a type parameter named Action which is a comparable type. This is defined to let you define your state in a Reducer pattern. Also we recommend that your state also implements sync.Locker interface to be thread-safe.

Counter Program with State

Lets first implement our State. We want to have a simple counter which counts the number of requests we received. Our EchoCounter state has action type of string and it supports two actions: up and down. The up action increases the counter and the down action decreases the counter. The following code shows the implementation of the EchoCounter state.

package main

import (
	"fmt"
	"strings"
	"sync"

	"github.com/clubpay/ronykit/rony"
)

type EchoCounter struct {
	sync.Mutex

	Count int
}

func (e *EchoCounter) Name() string {
	return "EchoCounter"
}

func (e *EchoCounter) Reduce(action string) error {
	switch strings.ToLower(action) {
	case "up":
		e.Count++
	case "down":
		if e.Count <= 0 {
			return fmt.Errorf("count cannot be negative")
		}
		e.Count--
	default:
		return fmt.Errorf("unknown action: %s", action)
	}

	return nil
}


Now We want to implement our handlers to handle UP and DOWN functionality. We need to first define DTOs (Data Transfer Objects) of our handlers. However instead of defining two separate DTOs, we going to define one DTO and use the Action field of the DTO to determine the action we want to perform. Although, this is just in our example, in your case you may come up with a better design.

package main

import (
	"github.com/clubpay/ronykit/rony"
)

type CounterRequestDTO struct {
	Action string `json:"action"`
	Count int `json:"count"`
}

type CounterResponseDTO struct {
	Count int `json:"count"`
}

Now we need to define our handler. Let's first define our handler struct.

package main

func count(ctx *rony.UnaryCtx[*EchoCounter, string], req *CounterRequestDTO) (*CounterResponseDTO, error) {
	res := &CounterResponseDTO{}
	err := ctx.ReduceState(
		req.Action,
		func(s *EchoCounter, err error) error {
			if err != nil {
				return rony.NewError(err).SetCode(http.StatusBadRequest)
			}

			res.Count = s.Count

			return nil
		},
	)
	if err != nil {
		return nil, err
	}

	return res, nil
}

As you can see, we have a function named count which has two parameters. The first parameter is a UnaryCtx which is a generic type and provides the state to the handler. It also has many more helper methods which we will discuss later. The second parameter is the request DTO. The return value of the handler is the response DTO and an error. The handler function is a normal function and it is not a method of a struct. Inside the handler code, you see we have ReduceState method which is a helper method that let us to mutate the state in an atomic fashion. The code in the callback function of the ReduceState method is executed in a thread-safe manner and it is guaranteed that no other goroutine is mutating the state while the callback function is running.

Now let's wrap up the code and define our server.

package main

import (
    "context"
    "os"

    "github.com/clubpay/ronykit/rony"
)

func main() {
    srv := rony.NewServer(
        rony.Listen(":80"),
        rony.WithServerName("CounterServer"),
    )

    // Set up the server with the initial state, which is a pointer to EchoCounter
    // We can have as many states as we want. But each handler can only work with
    // one state. In other words, we cannot register one handler with two different
    // setup contexts.
    rony.Setup(
        srv,
        "CounterService",
        rony.ToInitiateState[*EchoCounter, string](
            &EchoCounter{
                Count: 0,
            },
        ),
        // Register the count handler for both GET /count and GET /count/{action}
        // This way all the following requests are valid:
        // 1. GET /count/up&count=1
        // 2. GET /count/down&count=2
        // 3. GET /count?action=up&count=1
        // 4. GET /count?action=down&count=2
        rony.WithUnary(
            count,
            rony.GET("/count/{action}"),
            rony.GET("/count"),
        ),
    )

    // Run the server in blocking mode
    err := srv.Run(
        context.Background(),
        os.Kill, os.Interrupt,
    )
    if err != nil {
        panic(err)
    }
}

We first create a new server and then we set up the server with the initial state. We can have as many states as we want. Then we register our handler with the server. We can register as many handlers as we want. Finally we run the server in blocking mode. You can also check examples to see more examples.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Setup

func Setup[S State[A], A Action](
	srv *Server,
	name string,
	initState InitState[S, A],
	opt ...SetupOption[S, A],
)

Setup is a helper function to set up server and services. S **MUST** implement State[A] and also **MUST** be a pointer to a struct, otherwise this function panics Possible options are: - WithState: to set up state - WithUnary: to set up unary handler - WithStream: to set up stream handler

Types

type Action

type Action comparable

State related types

type BaseCtx added in v0.11.19

type BaseCtx[S State[A], A Action] struct {
	// contains filtered or unexported fields
}

BaseCtx is a base context object used by UnaryCtx and StreamCtx to provide common functionality.

func (*BaseCtx[S, A]) Conn added in v0.11.19

func (c *BaseCtx[S, A]) Conn() kit.Conn

func (*BaseCtx[S, A]) Context added in v0.11.19

func (c *BaseCtx[S, A]) Context() context.Context

func (*BaseCtx[S, A]) Exists added in v0.11.23

func (c *BaseCtx[S, A]) Exists(key string) bool

func (*BaseCtx[S, A]) Get added in v0.11.23

func (c *BaseCtx[S, A]) Get(key string) any

func (*BaseCtx[S, A]) GetInHdr added in v0.11.23

func (c *BaseCtx[S, A]) GetInHdr(key string) string

GetInHdr is shorthand for GetInputHeader

func (*BaseCtx[S, A]) GetInputHeader added in v0.11.23

func (c *BaseCtx[S, A]) GetInputHeader(key string) string

func (*BaseCtx[S, A]) KitCtx added in v0.11.22

func (c *BaseCtx[S, A]) KitCtx() *kit.Context

KitCtx returns the underlying kit.Context. This is useful when we need to pass the context into inner layers of our application when we need to have access to more generic functionalities.

func (*BaseCtx[S, A]) Next added in v0.11.19

func (c *BaseCtx[S, A]) Next()

func (*BaseCtx[S, A]) ReduceState added in v0.11.19

func (c *BaseCtx[S, A]) ReduceState(action A, fn func(s S, err error) error) (err error)

ReduceState is a helper function to reduce state and call fn if it's not nil. If you need to reduce the state in an atomic fashion, then you should pass a function fn which is guaranteed to be called in a locked state. Although, it only works if S implements sync.Locker interface.

func (*BaseCtx[S, A]) Route added in v0.11.19

func (c *BaseCtx[S, A]) Route() string

func (*BaseCtx[S, A]) Set added in v0.11.23

func (c *BaseCtx[S, A]) Set(key string, value any)

func (*BaseCtx[S, A]) SetUserContext added in v0.11.19

func (c *BaseCtx[S, A]) SetUserContext(userCtx context.Context)

func (*BaseCtx[S, A]) State added in v0.11.19

func (c *BaseCtx[S, A]) State() S

func (*BaseCtx[S, A]) StopExecution added in v0.11.19

func (c *BaseCtx[S, A]) StopExecution()

StopExecution stops the execution so the next middleware/handler won't be called.

func (*BaseCtx[S, A]) Walk added in v0.11.23

func (c *BaseCtx[S, A]) Walk(f func(key string, val any) bool)

func (*BaseCtx[S, A]) WalkInHdr added in v0.11.23

func (c *BaseCtx[S, A]) WalkInHdr(f func(key string, val string) bool)

WalkInHdr is shorthand for WalkInputHeader

func (*BaseCtx[S, A]) WalkInputHeader added in v0.11.23

func (c *BaseCtx[S, A]) WalkInputHeader(f func(key string, val string) bool)

type CORSConfig

type CORSConfig = fasthttp.CORSConfig

type CompressionLevel

type CompressionLevel = fasthttp.CompressionLevel
const (
	CompressionLevelDisabled        CompressionLevel = -1
	CompressionLevelDefault         CompressionLevel = 0
	CompressionLevelBestSpeed       CompressionLevel = 1
	CompressionLevelBestCompression CompressionLevel = 2
)

Represents compression level that will be used in the middleware

type EMPTY added in v0.11.19

type EMPTY struct{}

EMPTY is a special State that does nothing. This is a utility object when we don't to have a shared state in our service.

func (EMPTY) Name added in v0.11.19

func (e EMPTY) Name() string

func (EMPTY) Reduce added in v0.11.19

func (EMPTY) Reduce(action NOP) error

type Error

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

func NewError

func NewError(err error) Error

func (Error) Error

func (e Error) Error() string

func (Error) GetCode

func (e Error) GetCode() int

func (Error) GetItem

func (e Error) GetItem() string

func (Error) SetCode

func (e Error) SetCode(code int) Error

func (Error) SetItem

func (e Error) SetItem(item string) Error

type InitState

type InitState[S State[A], A Action] func() S

func EmptyState added in v0.11.19

func EmptyState() InitState[EMPTY, NOP]

EmptyState is a helper function to create an empty state. This is a noop state that does nothing; whenever you don't need a state, you can use this function to create one.

func ToInitiateState

func ToInitiateState[S State[A], A Action](s S) InitState[S, A]

type Message

type Message kit.Message

Alias types

type Middleware added in v0.11.19

type Middleware[S State[A], A Action] interface {
	StatefulMiddleware[S, A] | StatelessMiddleware
}

type NOP added in v0.11.19

type NOP struct{}

NOP is a special Action that does nothing. This is a utility object when we use EMPTY state.

type NodeSelectorFunc added in v0.11.24

type NodeSelectorFunc = kit.EdgeSelectorFunc

Alias types

type PushOpt

type PushOpt func(e *kit.Envelope)

func WithHdr

func WithHdr(key, value string) PushOpt

func WithHdrMap

func WithHdrMap(hdr map[string]string) PushOpt

type RESTParam

type RESTParam = fasthttp.Param

Alias types

type RESTParams

type RESTParams = fasthttp.Params

Alias types

type Selector

type Selector kit.RouteSelector

Alias types

type Server

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

func NewServer

func NewServer(opts ...ServerOption) *Server

func (*Server) ExportDesc added in v0.11.23

func (s *Server) ExportDesc() []desc.ServiceDesc

ExportDesc returns all services descriptions.

func (*Server) Run

func (s *Server) Run(ctx context.Context, signals ...os.Signal) error

Run the service in blocking mode. If you need more control over the lifecycle of the service, you can use the Start and Stop methods.

func (*Server) Start

func (s *Server) Start(ctx context.Context) error

func (*Server) Stop

func (s *Server) Stop(ctx context.Context, signals ...os.Signal)

type ServerOption

type ServerOption func(cfg *serverConfig)

func Listen

func Listen(addr string) ServerOption

func WithCORS

func WithCORS(cors CORSConfig) ServerOption

func WithCompression

func WithCompression(lvl CompressionLevel) ServerOption

func WithDisableHeaderNamesNormalizing added in v0.11.31

func WithDisableHeaderNamesNormalizing() ServerOption

func WithGlobalHandlers added in v0.11.21

func WithGlobalHandlers(handlers ...kit.HandlerFunc) ServerOption

func WithLogger added in v0.11.21

func WithLogger(logger kit.Logger) ServerOption

func WithPredicateKey

func WithPredicateKey(key string) ServerOption

func WithPrefork added in v0.11.21

func WithPrefork() ServerOption

func WithServerName

func WithServerName(name string) ServerOption

func WithShutdownTimeout added in v0.11.21

func WithShutdownTimeout(timeout time.Duration) ServerOption

func WithTracer added in v0.11.21

func WithTracer(tracer kit.Tracer) ServerOption

func WithWebsocketEndpoint

func WithWebsocketEndpoint(endpoint string) ServerOption

type SetupContext

type SetupContext[S State[A], A Action] struct {
	// contains filtered or unexported fields
}

SetupContext is a context object holds data until the Server starts. It is used internally to hold state and server configuration.

type SetupOption added in v0.11.19

type SetupOption[S State[A], A Action] func(ctx *SetupContext[S, A])

func WithContract added in v0.11.22

func WithContract[S State[A], A Action](
	contract *desc.Contract,
) SetupOption[S, A]

WithContract is a SetupOption to set up a legacy desc.Contract directly. This method is useful when you are migrating your old code to rony.

func WithCoordinator added in v0.11.24

func WithCoordinator[S State[A], A Action](sel NodeSelectorFunc) SetupOption[S, A]

func WithMiddleware added in v0.11.19

func WithMiddleware[S State[A], A Action, M Middleware[S, A]](
	m ...M,
) SetupOption[S, A]

func WithStream added in v0.11.19

func WithStream[S State[A], A Action, IN, OUT Message](
	h StreamHandler[S, A, IN, OUT],
	opt ...StreamOption,
) SetupOption[S, A]

WithStream is a SetupOption to set up stream handler. Possible options are: - RPC: to set up RPC handler

func WithUnary added in v0.11.19

func WithUnary[S State[A], A Action, IN, OUT Message](
	h UnaryHandler[S, A, IN, OUT],
	opt ...UnaryOption,
) SetupOption[S, A]

WithUnary is a SetupOption to set up unary handler. Possible options are: - REST: to set up REST handler - GET: to set up GET handler - POST: to set up POST handler - PUT: to set up PUT handler - DELETE: to set up DELETE handler - PATCH: to set up PATCH handler - HEAD: to set up HEAD handler - OPTIONS: to set up OPTIONS handler

type State

type State[A Action] interface {
	Name() string
	Reduce(action A) error
}

State related types

type StatefulMiddleware added in v0.11.21

type StatefulMiddleware[S State[A], A Action] func(ctx *BaseCtx[S, A])

type StatelessMiddleware added in v0.11.19

type StatelessMiddleware = kit.HandlerFunc

type StreamCtx

type StreamCtx[S State[A], A Action, M Message] struct {
	BaseCtx[S, A]
}

func (*StreamCtx[S, A, M]) Push

func (c *StreamCtx[S, A, M]) Push(m M, opt ...PushOpt) S

func (*StreamCtx[S, A, M]) PushTo

func (c *StreamCtx[S, A, M]) PushTo(conn kit.Conn, m M, opt ...PushOpt)

type StreamHandler

type StreamHandler[
	S State[A], A Action,
	IN, OUT Message,
] func(ctx *StreamCtx[S, A, OUT], in IN) error

type StreamOption

type StreamOption = stream.Option

Exposing internal types

func RPC

func RPC(predicate string, opt ...StreamSelectorOption) StreamOption

RPC is a StreamOption to set up RPC handler.

type StreamSelectorOption

type StreamSelectorOption = stream.SelectorOption

Exposing internal types

type UnaryCtx

type UnaryCtx[S State[A], A Action] struct {
	BaseCtx[S, A]
}

func (*UnaryCtx[S, A]) RESTConn

func (c *UnaryCtx[S, A]) RESTConn() (kit.RESTConn, bool)

RESTConn returns the underlying RESTConn if the connection is RESTConn.

func (*UnaryCtx[S, A]) SetOutHdr added in v0.11.23

func (c *UnaryCtx[S, A]) SetOutHdr(key, value string)

SetOutHdr is shorthand for SetOutputHeader

func (*UnaryCtx[S, A]) SetOutHdrMap added in v0.11.23

func (c *UnaryCtx[S, A]) SetOutHdrMap(kv map[string]string)

SetOutHdrMap is shorthand for SetOutputHeaderMap

func (*UnaryCtx[S, A]) SetOutputHeader added in v0.11.23

func (c *UnaryCtx[S, A]) SetOutputHeader(key, value string)

SetOutputHeader sets the output header

func (*UnaryCtx[S, A]) SetOutputHeaderMap added in v0.11.23

func (c *UnaryCtx[S, A]) SetOutputHeaderMap(kv map[string]string)

SetOutputHeaderMap sets the output header map

type UnaryHandler

type UnaryHandler[
	S State[A], A Action,
	IN, OUT Message,
] func(ctx *UnaryCtx[S, A], in IN) (*OUT, error)

type UnaryOption

type UnaryOption = unary.Option

Exposing internal types

func ALL added in v0.11.30

func ALL(path string, opt ...UnarySelectorOption) UnaryOption

func DELETE

func DELETE(path string, opt ...UnarySelectorOption) UnaryOption

func GET

func GET(path string, opt ...UnarySelectorOption) UnaryOption
func HEAD(path string, opt ...UnarySelectorOption) UnaryOption

func OPTIONS

func OPTIONS(path string, opt ...UnarySelectorOption) UnaryOption

func PATCH

func PATCH(path string, opt ...UnarySelectorOption) UnaryOption

func POST

func POST(path string, opt ...UnarySelectorOption) UnaryOption

func PUT

func PUT(path string, opt ...UnarySelectorOption) UnaryOption

func REST

func REST(method, path string, opt ...UnarySelectorOption) UnaryOption

type UnarySelectorOption

type UnarySelectorOption = unary.SelectorOption

Exposing internal types

func UnaryName

func UnaryName(name string) UnarySelectorOption

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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