hexa

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2022 License: MIT Imports: 15 Imported by: 42

README

Hexa is a microservice SDK.

Note : Hexa is in progress, it does not have stable version until now, so use it at your own risk.

Requirements:

go : minimum version 1.13

Install

go get github.com/kamva/hexa

Available Services:

How to use:

example:

// Assume we want to use viper as config service.
v := viper.New()

// tune your viper.
config := hexaconfig.NewViperDriver(v)

// Use config service in app.

Proposal

  • Replace http status code with gRPC status code in our errors (also maybe in replies).

  • Implement Hexa Context propagators for distributed tracing.

  • Implement a service (e.g Health) which should implement by all Hexa services to check health of that service [Accepted][Implemented].

Notes

  • Run go generate ./db/... or go generate ./... to generate some files after your changes in the DB models.

Todo

  • Where are we checking log level on logger initialization step?
  • Change all of kamva packages from uppercase to lowercase.
  • Write Tests
  • Add badges to readme.
  • CI for tests.

Documentation

Index

Constants

View Source
const (
	UserMetaKeyUserType   = "_user_type"
	UserMetaKeyIdentifier = "_user_identifier"
	UserMetaKeyEmail      = "_user_email"
	UserMetaKeyPhone      = "_user_phone"
	UserMetaKeyName       = "_user_name"
	UserMetaKeyUsername   = "_user_username"
	UserMetaKeyIsActive   = "_user_is_active"
	UserMetaKeyRoles      = "_user_roles"
)

User meta keys.

View Source
const (
	// ErrKeyInternalError is the internal error key in Error
	// messages over all of packages. use this to have just one
	// internal_error translation key in your translation system.
	// TODO: remove this key if we don't use it in our projects.
	ErrKeyInternalError = "lib.internal_error"
)
View Source
const SessionContextKey = "_ctx_session"
View Source
const TranslateKeyEmptyMessage = "_empty_message"

TranslateKeyEmptyMessage is special key that translators return empty string for that.

View Source
const (
	Version = "1.1.0"
)

Version is Hexa current version.

Variables

View Source
var (
	ErrInvalidID = NewError(http.StatusBadRequest, "lib.entity.invalid_id").SetError(errors.New("id value is invalid"))
)
View Source
var ErrLockAlreadyAcquired = errors.New("lock already acquired")

Functions

func CtxBaseLogger

func CtxBaseLogger(ctx context.Context) hlog.Logger

func CtxCorrelationId

func CtxCorrelationId(ctx context.Context) string

func CtxLocale

func CtxLocale(ctx context.Context) string

func CtxLogger

func CtxLogger(ctx context.Context) hlog.Logger

CtxLogger returns the context logger.

func CtxRequest

func CtxRequest(ctx context.Context) *http.Request

func ErrFields

func ErrFields(err error) []hlog.Field

ErrFields checks if the provided error is a Hexa error, returns hexa error fields, otherwise returns regular error fields.

func Logger

func Logger(ctx context.Context) hlog.Logger

Logger tries to get logger from the context, otherwise returns the default logger.

func NewContext

func NewContext(ctx context.Context, p ContextParams) context.Context

NewContext returns new hexa Context.

func PrintBanner

func PrintBanner(banner, product, version, website string)

PrintBanner print the app's banner

func Validate

func Validate(rules ...Rule) error

Validate validates the provided rules and returns first broken rule's error. otherwise it returns nil.

func ValidateWithContext

func ValidateWithContext(ctx context.Context, rules ...RuleWithContext) error

ValidateWithContext validates rules with a context.

func WithBaseLogger

func WithBaseLogger(ctx context.Context, l hlog.Logger) context.Context

WithBaseLogger sets the base logger in the context. when something change in the context we'll use this base logger to update the logger

func WithBaseTranslator

func WithBaseTranslator(ctx context.Context, t Translator) context.Context

WithBaseTranslator sets the base Translator in the context. when you change the locale, we use the base translator to set the new translator.

func WithCorrelationId

func WithCorrelationId(ctx context.Context, cid string) context.Context

func WithLocale

func WithLocale(ctx context.Context, locale string) context.Context

func WithLogger

func WithLogger(ctx context.Context, l hlog.Logger) context.Context

WithLogger sets the final logger, this will overwrites the auto created logger with extra fields. If you want to set the base logger and auto update on every change in the context, use WithBaseLogger instead of WithLogger.

func WithPropagator

func WithPropagator(multi ContextPropagator, p ContextPropagator) error

WithPropagator add another propagator to ourself implemented multiPropagator.

func WithRequest

func WithRequest(ctx context.Context, r *http.Request) context.Context

func WithStore

func WithStore(ctx context.Context, s Store) context.Context

func WithTranslator

func WithTranslator(ctx context.Context, t Translator) context.Context

WithTranslator sets the final localized translator. Use WithBaseTranslator if you want to set the base translator and auto update the localized translator on every change on the context's "locale" field.

func WithUser

func WithUser(ctx context.Context, u User) context.Context

Types

type Bootable

type Bootable interface {
	Boot() error
}

type Config

type Config interface {
	// Unmarshal unmarshal config values to the provided struct.
	Unmarshal(instance any) error
}

type ContextParams

type ContextParams struct {
	Request        *http.Request
	CorrelationId  string
	Locale         string // Locale syntax is just same as HTTP Accept-Language header.
	User           User
	BaseLogger     hlog.Logger
	BaseTranslator Translator
	Store          Store // Optional
}

type ContextPropagator

type ContextPropagator interface {
	// Inject injects values from context into a map and returns the map.
	Inject(context.Context) (map[string][]byte, error)

	// Extract extract values from map and insert to the context.
	Extract(context.Context, map[string][]byte) (context.Context, error)
}

func NewContextPropagator

func NewContextPropagator(l hlog.Logger, t Translator) ContextPropagator

NewContextPropagator returns new context propagator to propagate the Hexa context itself.

func NewKeysPropagator

func NewKeysPropagator(keys []string, strict bool) ContextPropagator

func NewMultiPropagator

func NewMultiPropagator(propagators ...ContextPropagator) ContextPropagator

type DLM

type DLM interface {
	// NewMutex returns new mutex with provided key
	// and default owner and ttl.
	NewMutex(Key string) Mutex

	// NewMutexWithTTL creates a new mutex with
	// the provided key and ttl.
	NewMutexWithTTL(key string, ttl time.Duration) Mutex

	// NewMutexWithOptions returns new mutex with
	// provided options.
	NewMutexWithOptions(o MutexOptions) Mutex
}

DLM is distributed lock manager.

type Descriptor

type Descriptor struct {
	Name     string
	Instance Service
	Priority int
}

Descriptor describes the service.

type Error

type Error interface {
	error

	// SetError set the internal error.
	SetError(error) Error

	// InternalError returns the internal error.
	InternalError() error

	//Is function satisfy Is interface of errors package.
	Is(error) bool

	// HTTPStatus returns the http status code for the Error.
	HTTPStatus() int

	// SetHTTPStatus sets the http status code for the reply.
	SetHTTPStatus(status int) Error

	// ID is error's identifier. its format should be
	// something like "product.variant.not_found" or "lib.jwt.not_found" .
	// as a convention we prefix our base packages (all hexa packages) with "lib".
	ID() string

	// Localize localize te message for you.
	// you can store the gRPC localized error
	// message and return it by this method.
	Localize(t Translator) (string, error)

	// Data returns the extra data of the Error (e.g show this data to user).
	// Note: we use data as translation prams also.
	Data() Map

	// SetData set the Error data as extra data of the Error to show to the user.
	SetData(data Map) Error

	// ReportData returns the data that should use on reporting Error to somewhere (e.g log aggregator)
	ReportData() Map

	SetReportData(data Map) Error

	// ReportIfNeeded function report the Error to the log system if
	// http status code is in range 5XX.
	// return value specify that reported or no.
	ReportIfNeeded(hlog.Logger, Translator) bool
}

Error is reply to actions when occur error in microservices.

func AsHexaErr

func AsHexaErr(err error) Error

AsHexaErr check whether provided error can be used as hexa error or not. by this method, we don't need to use guilt.CauseErr and then check if caused error is hexa error or not, and we can simply like any other error implement the errors.Unwrap interface on our Error interface. Why we don't use errors.As() method? We can use it also, these two methods do not conflict each other, it gets a new instance of target and check target's type and then if found error with that type in the chain, assign its value to the target, but here we want to just return our hexa error (with any implementation of hexa error) to the user.

func NewError

func NewError(httpStatus int, id string) Error

NewError returns new instance the Error interface.

func NewLocalizedError

func NewLocalizedError(status int, id string, localizedMsg string, err error) Error

NewLocalizedError returns new instance the Error interface.

type Health

type Health interface {
	HealthIdentifier() string
	LivenessStatus(ctx context.Context) LivenessStatus
	ReadinessStatus(ctx context.Context) ReadinessStatus
	HealthStatus(ctx context.Context) HealthStatus
}

func NewPingHealth

func NewPingHealth(l hlog.Logger, identifier string, ping Ping, tags map[string]string) Health

type HealthReport

type HealthReport struct {
	Alive    LivenessStatus  `json:"alive"`
	Ready    ReadinessStatus `json:"ready"`
	Statuses []HealthStatus  `json:"statuses"`
}

type HealthReporter

type HealthReporter interface {
	AddLivenessChecks(l ...Health) HealthReporter
	AddReadinessChecks(l ...Health) HealthReporter
	AddStatusChecks(l ...Health) HealthReporter
	AddToChecks(l ...Health) HealthReporter

	LivenessStatus(ctx context.Context) LivenessStatus
	ReadinessStatus(ctx context.Context) ReadinessStatus
	HealthReport(ctx context.Context) HealthReport
}

func NewHealthReporter

func NewHealthReporter() HealthReporter

type HealthStatus

type HealthStatus struct {
	Id    string            `json:"id"`
	Alive LivenessStatus    `json:"alive"`
	Ready ReadinessStatus   `json:"ready"`
	Tags  map[string]string `json:"tags,omitempty"`
}

func HealthCheck

func HealthCheck(l ...Health) []HealthStatus

type HttpRespBody

type HttpRespBody struct {
	Code    string `json:"code"`
	Message string `json:"message,omitempty"`
	Data    any    `json:"data,omitempty"`
	Debug   any    `json:"debug,omitempty"` // Set this value to nil when you are on production mode.
}

HttpRespBody is the http response body format

func (HttpRespBody) MarshalEasyJSON

func (v HttpRespBody) MarshalEasyJSON(w *jwriter.Writer)

MarshalEasyJSON supports easyjson.Marshaler interface

func (HttpRespBody) MarshalJSON

func (v HttpRespBody) MarshalJSON() ([]byte, error)

MarshalJSON supports json.Marshaler interface

func (*HttpRespBody) UnmarshalEasyJSON

func (v *HttpRespBody) UnmarshalEasyJSON(l *jlexer.Lexer)

UnmarshalEasyJSON supports easyjson.Unmarshaler interface

func (*HttpRespBody) UnmarshalJSON

func (v *HttpRespBody) UnmarshalJSON(data []byte) error

UnmarshalJSON supports json.Unmarshaler interface

type ID

type ID interface {
	fmt.Stringer

	// Validate specify provided id is valid or not.
	Validate(id any) error

	// From convert provided value to its id.
	// From will returns error if provided value
	// can not convert to an native id.
	From(id any) error

	// MustFrom Same as FromString but on occur error, it will panic.
	MustFrom(id any)

	// Val returns the native id value (e.g ObjectID in mongo, ...).
	Val() any

	// IsEqual say that two hexa id are equal or not.
	IsEqual(ID) bool
}

ID is the id of entities across the hexa packages. This is because hexa does not want to be dependent to specific type of id. (e.g mongo ObjectID, mysql integer,...)

type LivenessResult

type LivenessResult struct {
	Id     string `json:"id"`
	Status LivenessStatus
}

type LivenessStatus

type LivenessStatus string
const (
	StatusAlive LivenessStatus = "ALIVE"
	StatusDead  LivenessStatus = "DEAD"
)

func AliveStatus

func AliveStatus(l ...HealthStatus) LivenessStatus

type Map

type Map map[string]any

Map defines a well-known Golang map: map[string]any

type Mutex

type Mutex interface {
	// Lock try to lock or wait for release and then lock.
	// We can have multiple behaviors when Lock invoke multiple times after locked one time:
	// 1. try to refresh the lock
	// 2. return error
	// 3. return nil and ignore next calls.
	// behavior in our implementation should be 1, means
	// you should try to refresh lock when user call
	// this method again.
	Lock(ctx context.Context) error

	// TryLock tries to lock or returns the ErrLockAlreadyAcquired
	// error if it acquired.
	// Please note different mutexes with same lock name and same
	// owner can lock and unlock each other.
	// expiry date should begin after call to this method, not at the
	// creation time of this mutex.
	// We can have multiple behaviors when TryLock invoke multiple times after locked one time:
	// 1. try to refresh the lock
	// 2. return error
	// 3. return nil and ignore next calls.
	// behavior in our implementation should be the option number 1, means
	// you should try to refresh lock when user calls to
	// this method again.
	TryLock(ctx context.Context) error

	// Unlock release the lock.
	// it should ignore if lock is already released
	// and do not return any error.
	Unlock(ctx context.Context) error
}

Mutex can be used as a Distributed lock.

type MutexOptions

type MutexOptions struct {
	Key string
	// If owner is empty, DLM uses default Owner.
	Owner string
	TTL   time.Duration
}

type Ping

type Ping func(ctx context.Context) error

type ReadinessResult

type ReadinessResult struct {
	Id     string `json:"id"`
	Status ReadinessStatus
}

type ReadinessStatus

type ReadinessStatus string
const (
	StatusReady   ReadinessStatus = "READY"
	StatusUnReady ReadinessStatus = "UNREADY"
)

func ReadyStatus

func ReadyStatus(l ...HealthStatus) ReadinessStatus

type Reply

type Reply interface {
	// HTTPStatus returns the http status code for the reply.
	HTTPStatus() int

	// SetHTTPStatus sets the http status code for the reply.
	SetHTTPStatus(status int) Reply

	// ID is reply identifier
	ID() string

	// Data returns the extra data of the reply (e.g show this data to user).
	// Note: we use data as translation prams also.
	Data() any

	// SetData set the reply data as extra data of the reply to show to the user.
	SetData(data any) Reply
}

Reply is reply to actions in microservices.

func NewReply

func NewReply(httpStatus int, id string) Reply

NewReply returns new instance the Reply interface implemented by defaultReply.

type Rule

type Rule func() error

Rule is a rule signature.

type RuleWithContext

type RuleWithContext func(ctx context.Context) error

func WrapRule

func WrapRule(r Rule) RuleWithContext

WrapRule wraps the Rule and returns a RuleWithContext.

type Runnable

type Runnable interface {
	Run() error
}

Runnable is for services that need to be ran in background.

type Secret

type Secret string

Use secret to string show as * in fmt package.

func (Secret) MarshalJSON

func (s Secret) MarshalJSON() ([]byte, error)

func (Secret) String

func (s Secret) String() string

type Service

type Service any // Currently Service interface does not needs to implement anything.

type ServiceRegistry

type ServiceRegistry interface {
	Register(name string, instance Service)
	RegisterByInstance(instance Service)
	Boot() error
	Shutdown(ctx context.Context) error
	ShutdownCh() chan struct{}

	// Descriptors returns descriptors ordered by their priority.
	Descriptors() []*Descriptor
	Descriptor(name string) *Descriptor
	// Service method should return nil if service not found.
	Service(name string) Service
}

type Session

type Session interface {
	// SessionID returns the session id.
	SessionID() string

	// Expiry returns the session expiry time.
	Expiry() time.Time

	// SetExpiry sets the session expiry time. to delete a
	// session just set its expiry in before and save it.
	SetExpiry(at time.Time) error

	// Get returns the session value. it must be nil
	// if isn't found.
	Get(key string) (any, error)

	// Set sets the key and value in the session. nil
	// value meaning delete the key from the session.
	Set(key string, val any) error

	Save(ctx context.Context) error
}

func SessionFromContext

func SessionFromContext(ctx context.Context) Session

SessionFromContext extracts the session from the context. It will be nil if not found in the context.

type SessionProvider

type SessionProvider interface {
	// Get returns a session if it exists.
	// Sometimes we need to single store per session (e.g., cookieSessions), and
	// sometimes single store per all sessions (e.g., redis), by this pattern
	// (providing store at creation time) we support both.
	Get(store SessionStore, id string) (Session, error)

	// GetOrNew will returns old session or creates a new one with the provided ID.
	// id could be empty if you want to get just a new one session.
	GetOrNew(store SessionStore, id string) (Session, error)

	// Copy copies the session with a new ID.
	Copy() (Session, error)
}

type SessionStore

type SessionStore interface {
	Save(ctx context.Context, sess ...Session) error
}

type Shutdownable

type Shutdownable interface {
	Shutdown(context.Context) error
}

type Store

type Store interface {
	Get(key string) any
	Set(key string, val any)
	SetIfNotExist(key string, val func() any) any
}

Store is actually a concurrency-safe map.

func CtxStore

func CtxStore(ctx context.Context) Store

type Translator

type Translator interface {
	// Localize function returns new localized translator function.
	Localize(langs ...string) Translator

	// Translate get key and params nad return translation.
	Translate(key string, keyParams ...any) (string, error)

	// MustTranslate get key and params and translate,
	//otherwise panic relative error.
	MustTranslate(key string, keyParams ...any) string

	// TranslateDefault translate with default message.
	TranslateDefault(key string, fallback string, keyParams ...any) (string, error)

	// MustTranslateDefault translate with default message, on occur error,will panic it.
	MustTranslateDefault(key string, fallback string, keyParams ...any) string
}

func CtxBaseTranslator

func CtxBaseTranslator(ctx context.Context) Translator

func CtxTranslator

func CtxTranslator(ctx context.Context) Translator

type User

type User interface {
	// Type specifies user's type (guest,regular,...)
	Type() UserType

	// Identifier returns user's identifier
	Identifier() string

	// Email returns the user's email.
	// This value can be empty.
	Email() string

	// Phone returns the user's phone number.
	// This value can be empty.
	Phone() string

	// Name returns the user name.
	Name() string

	// Username can be unique username,email,phone number or
	// everything else which can be used as username.
	Username() string

	// IsActive specify that user is active or no.
	IsActive() bool

	// Roles returns user's roles.
	Roles() []string

	Meta(key string) (val any, exists bool)

	SetMeta(key string, val any) (User, error)

	// User must be able be export and import using this meta data.
	// Meta data must be json serializable.
	MetaData() Map
}

User who sends request to the app (can be guest,regular user,service user,...)

func CtxUser

func CtxUser(ctx context.Context) User

func MustNewUserFromMeta

func MustNewUserFromMeta(meta Map) User

func NewGuest

func NewGuest() User

NewGuest returns new instance of guest user.

func NewServiceUser

func NewServiceUser(id, name string, isActive bool, roles []string) User

NewServiceUser returns new instance of Service user.

func NewUser

func NewUser(p UserParams) User

NewUser returns new hexa user instance.

func NewUserFromMeta

func NewUserFromMeta(meta Map) (User, error)

NewUserFromMeta creates new user from the meta keys.

func WithUserRole

func WithUserRole(u User, role string) User

type UserParams

type UserParams struct {
	Id       string
	Type     UserType
	Email    string
	Phone    string
	Name     string
	UserName string
	IsActive bool
	Roles    []string
}

type UserPropagator

type UserPropagator interface {
	ToBytes(User) ([]byte, error)
	FromBytes([]byte) (User, error)
}

func NewUserPropagator

func NewUserPropagator() UserPropagator

type UserType

type UserType string

UserType is type of a user. possible values is : guest: Use for guest users. regular: Use for regular users of app (real registered users) service: Use for service users (microservices,...)

const (
	UserTypeGuest   UserType = "_guest"
	UserTypeRegular UserType = "_regular"
	UserTypeService UserType = "_service"
)

Directories

Path Synopsis
db
examples
hdlm
Package pagination provides support for pagination requests and responses.
Package pagination provides support for pagination requests and responses.
Package sr implements service registry.
Package sr implements service registry.

Jump to

Keyboard shortcuts

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