rony

package module
v0.14.9 Latest Latest
Warning

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

Go to latest
Published: May 28, 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, leveraging the power of Go generics to provide an easy-to-use and robust codebase that helps detect common mistakes at compile time. If you need more control over your code, such as selecting your own Gateway and Cluster, you can use the Kit Package. However, for most use cases, we recommend using the rony package.

Installation

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

First, install Go (version 1.19+ is required). Then use the following command to install RonyKIT:

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

Quick Start

State

When developing API servers, you often have a common state that can be shared between several of your endpoints (i.e., Contracts). For example, you might have a database or cache connection that you want to share between your endpoints. Additionally, you might want a shared state like a counter for the requests received or, in a simple chat application, to keep a list of connected users. Rony allows you to define your own state and provide it to your handlers, enabling access without relying on global variables or defining handler functions as methods of common structs. These approaches can be problematic as your project grows.

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

package rony

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

The State is a generic type with a type parameter named Action, which is a comparable type. This design allows you to define your state in a Reducer pattern. We also recommend that your state implements the sync.Locker interface to be thread-safe.

Counter Program with State

Let's first implement our State. We want a simple counter that counts the number of requests received. Our EchoCounter state has an action type of string and supports two actions: up and down. The up action increases the counter, and the down action decreases it. 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
}

Next, we need to implement our handlers to handle UP and DOWN functionality. We'll define DTOs (Data Transfer Objects) for our handlers. Instead of defining two separate DTOs, we'll define one DTO and use the Action field to determine the action to perform.

package main

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

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

Now we define our handler.

package main

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

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
}

Our handler function, count, has two parameters. The first is a UnaryCtx, a generic type that provides the state to the handler, along with many helper methods. The second parameter is the request DTO. The handler returns the response DTO and an error. The ReduceState method in the handler allows us to mutate the state in an atomic fashion. The code in the ReduceState callback function executes in a thread-safe manner, ensuring no other goroutine mutates the state simultaneously.

Finally, 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, a pointer to EchoCounter
  // We can have multiple states, but each handler works with only one state.
  // In other words, we cannot register one handler with two different states.
  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, 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 set up the server with the initial state. Multiple states can be set up, but each handler works with only one state. Then we register our handler with the server, allowing multiple handlers to be registered. Finally, we run the server in blocking mode. For more examples, check the examples directory.

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