spring

package module
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Jan 8, 2021 License: MIT Imports: 28 Imported by: 0

README

codecov Go Report Card MIT license

spring

Simple Framework designed to build scalable GraphQL services
Main features:
Create spring instance
package main
import "github.com/summer-solutions/spring"

func main() {
    registry := spring.New("app_name").Build()
    //Starting from now you have access to global DI container (DIC)
    container := DIC()
}

Starting GraphQL Server
package main
import "github.com/summer-solutions/spring"

func main() {
	
    graphQLExecutableSchema := ... // setup graphql.ExecutableSchema 
    ginHandler := // setup gin routes and middlewares
    // run http server
    spring.New("app_name").Build().RunServer(8080, graphQLExecutableSchema, ginHandler)
}

Setting server port

By default, spring server is using port defined in environment variable "PORT". If this variable is not set spring will use port number passed as fist argument.

Application name

When you setup server using New method yo must provide unique application name that can be checked in code like this:

    spring.New("app_name").Build()
    DIC().App().Name()
Setting mode

By default, spring is running in "spring.ModeLocal" mode. Mode is a string that is available in:

    spring.New("app_name").Build()
    // now you can access current spring mode
    DIC().App().Mode()

You can define spring mode using special environment variable "SPRING_MODE".

Spring provides by default two modes:

  • spring.ModeLocal
    • should be used on local development machine (developer laptop)
    • errors and stack trace is printed directly to system console
    • log level is set to Debug level
    • log is formatted using human friendly console text formatter
    • Gin Framework is running in GinDebug mode
  • spring.ModeProd
    • errors and stack trace is printed only using Log
    • log level is set to Warn level
    • log is formatted using json formatter

Mode is just a string. You can define any name you want. Remember that every mode that you create follows spring.ModeProd rules explained above.

In code you can easly check current mode using one of these methods:

    DIC().App().Mode()
    DIC().App().IsInLocalMode()
    DIC().App().IsInProdMode()
    DIC().App().IsInMode("my_mode")
Defining DI services

Spring builds global shared Dependency Injection container. You can register new services using this method:

package main
import "github.com/summer-solutions/spring"

func main() {
    spring.New("my_app").RegisterDIService(
      // put service definitions here
    )
}

Example of DI service definition:

package main
import (
    "github.com/summer-solutions/spring"
)
    
func main() {
    myService := &spring.ServiceDefinition{
        Name:   "my_service", // unique service key
        Global: true, // false if this service should be created as separate instance for each http request
        Build: func() (interface{}, error) {
            return &SomeService{}, nil // you can return any data you want
        },
        Close: func(obj interface{}) error { //optional
        },
        Flags: func(registry *spring.FlagsRegistry) { //optional
            registry.Bool("my-service-flag", false, "my flag description")
            registry.String("my-other-flag", "default value", "my flag description")
        },
    }
    
    // register it and run server
    spring.New("my_app").RegisterDIService(
      myService,
    )

    // you can access flags:
    val := spring.DIC().App().Flags().Bool("my-service-flag")
}

Now you can access this service in your code using:

import (
    "github.com/summer-solutions/spring"
)

func SomeResolver(ctx context.Context) {

    spring.HasService("my_service") // return true
    
    // return error if Build function returned error
    myService, has, err := spring.GetServiceSafe("my_service") 
    // will panic if Build function returns error
    myService, has := spring.GetServiceOptional("my_service") 
    // will panic if service is not registered or Build function returned errors
    myService := spring.GetServiceRequired("my_service") 

    // if you registered service with field "Global" set to false (request service)

    myContextService, has, err := spring.GetServiceForRequestSafe(ctx).Get("my_service_request")
    myContextService, has := spring.GetServiceForRequestOptional(ctx).Get("my_service_request") 
    myContextService := spring.GetServiceForRequestRequired(ctx).Get("my_service_request") 
}

It's a good practice to define one object to return all available services:

package my_package
import (
    "context"
    "github.com/summer-solutions/spring"
)

type dic struct {
}

var dicInstance = &dic{}

type DICInterface interface {
    MyService() *MyService
    MyOtherServiceForContext(ctx context.Context) *MyOtherService
}

func DIC() DICInterface {
    return dicInstance
}

func (d *dic) MyService() MyService {
    return spring.GetServiceRequired("service_key").(*MyService)
}

func (d *dic) MyOtherServiceForContext(ctx context.Context) MyOtherService {
    return spring.GetServiceForRequestRequired(ctx, "other_service_key").(*MyOtherService)
}

Running scripts

First You need to define script definition that implements spring.Script interface:


type TestScript struct {}

func (script *TestScript) Code() string {
    return "test-script"
}

func (script *TestScript) Unique() bool {
    // if true you can't run more than one script at the same time
    return false
}

func (script *TestScript) Description() string {
    return "script description"
}

func (script *TestScript) Run(ctx context.Context, exit spring.Exit) {
    // put logic here
	if shouldExitWithCode2 {
        exit.Error()	// you can exit script and specify exit code
    }
}

Methods above are required. Optionally you can also implement these interfaces:


// spring.ScriptInterval interface
func (script *TestScript) Interval() time.Duration {                                                    
    // run script every minute
    return time.Minute 
}

// spring.ScriptIntervalOptional interface
func (script *TestScript) IntervalActive() bool {                                                    
    // only run first day of month
    return time.Now().Day() == 1
}

// spring.ScriptIntermediate interface
func (script *TestScript) IsIntermediate() bool {                                                    
    // script is intermediate, for example is listening for data in chain
    return true
}

// spring.ScriptOptional interface
func (script *TestScript) Active() bool {                                                    
    // this script is visible only in local mode
    return DIC().App().IsInLocalMode()
}

Once you defined script you can run it using RunScript method:

package main
import "github.com/summer-solutions/spring"

func main() {
	spring.New("app_name").Build().RunScript(&TestScript{})
}

You can also register script as dynamic script and run it using program flag:

package main
import "github.com/summer-solutions/spring"

func main() {
	
    spring.New("app_name").RegisterDIService(
        &spring.ServiceDefinition{
            Name:   "my-script",
            Global: true,
            Script: true, // you need to set true here
            Build: func(ctn di.Container) (interface{}, error) {
                return &TestScript{}, nil
            },
        },
    ).Build()
}

You can see all available script by using special flag -list-scripts:

./app -list-scripts

To run script:

./app -run-script my-script

Documentation

Index

Constants

View Source
const ModeLocal = "local"
View Source
const ModeProd = "prod"
View Source
const ModeTest = "test"

Variables

This section is empty.

Functions

func GetServiceForRequestOptional

func GetServiceForRequestOptional(ctx context.Context, key string) (service interface{}, has bool)

func GetServiceForRequestRequired

func GetServiceForRequestRequired(ctx context.Context, key string) interface{}

func GetServiceForRequestSafe

func GetServiceForRequestSafe(ctx context.Context, key string) (service interface{}, has bool, err error)

func GetServiceOptional

func GetServiceOptional(key string) (service interface{}, has bool)

func GetServiceRequired

func GetServiceRequired(key string) interface{}

func GetServiceSafe

func GetServiceSafe(key string) (service interface{}, has bool, err error)

func GinFromContext

func GinFromContext(ctx context.Context) *gin.Context

func HasService

func HasService(key string) bool

func InitGin

func InitGin(server graphql.ExecutableSchema, ginInitHandler GinInitHandler) *gin.Engine

Types

type AppDefinition

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

func (*AppDefinition) Flags

func (app *AppDefinition) Flags() *Flags

func (*AppDefinition) IsInLocalMode

func (app *AppDefinition) IsInLocalMode() bool

func (*AppDefinition) IsInMode

func (app *AppDefinition) IsInMode(mode string) bool

func (*AppDefinition) IsInProdMode

func (app *AppDefinition) IsInProdMode() bool

func (*AppDefinition) IsInTestMode

func (app *AppDefinition) IsInTestMode() bool

func (*AppDefinition) Mode

func (app *AppDefinition) Mode() string

func (*AppDefinition) Name

func (app *AppDefinition) Name() string

type Config

type Config struct {
	*viper.Viper
}

type DICInterface

type DICInterface interface {
	App() *AppDefinition
	Log() apexLog.Interface
	Config() *Config
	OrmConfig() (orm.ValidatedRegistry, bool)
	OrmEngine() (*orm.Engine, bool)
	LogForContext(ctx context.Context) *RequestLog
	OrmEngineForContext(ctx context.Context) (*orm.Engine, bool)
}

func DIC

func DIC() DICInterface

type Exit

type Exit interface {
	Valid()
	Error()
	Custom(exitCode int)
}

type Flags

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

func (*Flags) Bool

func (f *Flags) Bool(name string) bool

func (*Flags) String

func (f *Flags) String(name string) string

type FlagsRegistry

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

func (*FlagsRegistry) Bool

func (r *FlagsRegistry) Bool(name string, value bool, usage string)

func (*FlagsRegistry) String

func (r *FlagsRegistry) String(name string, value string, usage string)

type GinInitHandler

type GinInitHandler func(ginEngine *gin.Engine)

type LogFieldProvider

type LogFieldProvider func() apexLog.Fielder

type LogRequestFieldProvider

type LogRequestFieldProvider func(ctx *gin.Context) apexLog.Fielder

type ORMRegistryInitFunc

type ORMRegistryInitFunc func(registry *orm.Registry)

type Registry

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

func New

func New(appName string) *Registry

func (*Registry) Build

func (r *Registry) Build() *Spring

func (*Registry) RegisterDIService

func (r *Registry) RegisterDIService(service ...*ServiceDefinition) *Registry

type RequestLog

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

func (*RequestLog) Log

func (l *RequestLog) Log(ctx *gin.Context) apexLog.Interface

type Script

type Script interface {
	Description() string
	Run(ctx context.Context, exit Exit)
	Unique() bool
}

type ScriptIntermediate

type ScriptIntermediate interface {
	IsIntermediate() bool
}

type ScriptInterval

type ScriptInterval interface {
	Interval() time.Duration
}

type ScriptIntervalOptional

type ScriptIntervalOptional interface {
	IntervalActive() bool
}

type ScriptOptional

type ScriptOptional interface {
	Active() bool
}

type ServiceDefinition

type ServiceDefinition struct {
	Name   string
	Global bool
	Script bool
	Build  func(ctn di.Container) (interface{}, error)
	Close  func(obj interface{}) error
	Flags  func(registry *FlagsRegistry)
}

func ServiceDefinitionOrmEngine

func ServiceDefinitionOrmEngine() *ServiceDefinition

func ServiceDefinitionOrmEngineForContext

func ServiceDefinitionOrmEngineForContext() *ServiceDefinition

func ServiceDefinitionOrmRegistry

func ServiceDefinitionOrmRegistry(init ORMRegistryInitFunc) *ServiceDefinition

func ServiceProviderConfigDirectory

func ServiceProviderConfigDirectory(configDirectory string) *ServiceDefinition

type Spring

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

func (*Spring) RunScript

func (s *Spring) RunScript(script Script)

func (*Spring) RunServer

func (s *Spring) RunServer(defaultPort uint, server graphql.ExecutableSchema, ginInitHandler GinInitHandler)

Directories

Path Synopsis
log

Jump to

Keyboard shortcuts

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