xin

package module
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2020 License: MIT Imports: 11 Imported by: 0

README

Xin

Go Report Card GoDoc

Xin is a framework focus on building configurable server service easily

It relies on many other fantastic repo as low-level implementation ,thanks for their author's work!

At a glance

HTTP server service

assume the following codes in example.go

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/spf13/cobra"
    "github.com/xieqiaoyu/xin"
    xhttp "github.com/xieqiaoyu/xin/http"
)

var configString = `
# app envirment
env = "dev"
#env = "release"

# http server setting
[http]
    listen=":8080"
`

//HttpDemoService Demo http service
type HttpDemoService struct{}

//RegisterRouter xhttp.ServerInterface implement
func (s *HttpDemoService) RegisterRouter(e *gin.Engine) error {
    e.Use(gin.Logger(), gin.Recovery())

    e.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    return nil
}

//InitializeHTTPServer define an instance of  xhttp.InitializeServerFunc
func InitializeHTTPServer() (xhttp.ServerInterface, error) {
    configLoader := xin.NewStringConfigLoader(configString, "toml")
    config := xin.NewConfig(configLoader, nil)
    //init toml config from a string
    config.Init()

    env := xin.NewEnvSetting(config)
    return xhttp.NewServer(env, config, &HttpDemoService{}), nil
}

func main() {
    httpCmd := xhttp.NewHTTPCmd(InitializeHTTPServer)

    rootCmd := &cobra.Command{}
    rootCmd.AddCommand(httpCmd)
    rootCmd.Execute()
}
# run the demo and visit 0.0.0.0:8080/ping on browser
$ go run example.go http

Getting Start

Installation

xin require go 1.13+

$ go get -u github.com/xieqiaoyu/xin
Command line

xin use cobra to implement app command line entry

xin provide some out-of-the-box subcommand generator such as

github.com/xieqiaoyu/xin/http.NewHTTPCmd

Add subcommands into a cobra Command makes the application flexible

import (
    "github.com/spf13/cobra"
    "github.com/xieqiaoyu/xin"
    xhttp "github.com/xieqiaoyu/xin/http"
)

...

func main() {
    httpCmd := xhttp.NewHTTPCmd(InitializeHTTPServer)
    versionCmd := xin.NewVersionCmd("v0.1.0-dev")


    rootCmd := &cobra.Command{}
    rootCmd.AddCommand(httpCmd)
    rootCmd.AddCommand(versionCmd)
    rootCmd.Execute()
}

read this doc for more usage about cobra

# call version subcommand to get application version
$ go run example.go version
v0.1.0-dev
Using config

Config is something could not be decided while coding or something should be assigned later.

A config can be in many form (file、string and so on) and have many type (jsonyamltoml).

Xin provide a config struct base on viper .

type Config struct{
  ...
}
func NewConfig(configloader ConfigLoader, configVerifier ConfigVerifier) *Config
//  create a new xin config
config := xin.NewConfig(configLoader,nil)

A config need a ConfigLoader and an optional ConfigVerifier

A ConfigLoader define how to load config

type ConfigLoader interface {
    LoadConfig(vc *viper.Viper) error
}

You can use your own config loader if you need . For convenience, xin provide several config loader

// load config from file
fileConfigLoader := xin.NewFileConfigLoader("another_config.toml","toml")
fileConfigLoader := xin.NewFileConfigLoader("another_config.json","json")

// load config from string
stringConfigLoader := xin.NewStringConfigLoader(configString, "toml")

Config should call Initbefore realy use , or there will be a panic

config := xin.NewConfig(configLoader,nil)
err := config.Init()

use the viper instance to get config setting, read this doc for more usage about viper

v := config.Viper()
httpListen := v.GetString("http.listen")
env := v.GetString("env")
HTTP server

The concept of http server in xin is a generator of net/http.Server.

Watch these definition in xin/http :

//package xin/http
import (
  "net/http"
  "github.com/spf13/cobra"
)

//ServerInterface a server can provide http server
type ServerInterface interface {
    // provide the http server service
    GetHTTPServer() *http.Server
}

//InitializeServerFunc an init http Server function gives the posibility for dependence inject
type InitializeServerFunc func() (ServerInterface, error)

//NewHTTPCmd Get a cobra command start http server
func NewHTTPCmd(getServer InitializeServerFunc) *cobra.Command

You can get a cobra command which can start http server by calling NewHTTPCmd

You need to provide a function to tell xin how to get the http server , beware the server we are talking about is a ServerInterface

Using xin http Server

Xin has an ServerInterface implementation : xin/http.Server , it use gin as a low-level implementation

//package github.com/xieqiaoyu/xin/http
import (
  "github.com/gin-gonic/gin"
  "github.com/xieqiaoyu/xin"
  "net/http"
)

//Service http service interface
type Service interface {
    // register route and middleware into gin engine
    RegisterRouter(*gin.Engine) error
}
//ServerConfig config provide HTTP server setting
type ServerConfig interface {
    HTTPListen() string
}

//NewServer Create a new HTTP server
func NewServer(env xin.Envirment, config ServerConfig, service Service) *Server

Interface Service register router and middleware into gin engine, you should implement your own service to complete the app.

xin.Envirment and ServerConfig are interface too , you can implement your own ,or you can use xin.EnvSetting and xin.Config directly

demo is an usage example

middlewares and tools for RESTful API

gin has already provide a good RESTful api develop experience , still we have to deal some annoying things

xin provide some handy middlewares and tools to make life easier

API response status

http status code often unable to meet our needs for api response status,for many times we need to give more detailed error code to let our client be able to handle subsequent logic

xin provide a solution to this scenario:

All api response body will return a status code and an optional error message like this

// return with http code 200
{
  "status": 200
  ...
}
// return with http code 400
{
  "status": 1400,
  "err_msg":"Bad api call"
  ...
}

we stipulate http code equal to status code mod 1000 ( status%1000 )

for example, we can have api return status code 1400、 2400、11400 ... with http code 400 (Bad Request) or 1403,5403 ... with http code 403(Forbidden)

To enable this feature in xin , first you should use WrapAPI middleware in service RegisterRouter

import (
  "github.com/gin-gonic/gin"
  mw "github.com/xieqiaoyu/xin/http/api/middleware"
  "github.com/xieqiaoyu/xin"
)
type HttpDemoService struct{
   Env     xin.Envirment
}
func (s *HttpDemoService) RegisterRouter(e *gin.Engine) error {
    e.Use(gin.Logger(), gin.Recovery())

    wrapAPIMiddleware := mw.XinRESTfulWrap(s.Env)

    e.GET("/ping",wrapAPIMiddleware,PingHandle)
    e.GET("/pingerror",wrapAPIMiddleware,PingErrorHandle)
    return nil
}
func PingHandle(c *gin.Context) {
  ...
}
func PingErrorHandle(c *gin.Context) {
  ...
}

then you can write api handle in this form

import (
  "github.com/gin-gonic/gin"
  "github.com/xieqiaoyu/xin/http/api"
)
func PingHandle(c *gin.Context) {
    api.SetStatus(1200).SetData(gin.H{
        "message": "pong",
    }).Apply(c)
    return
}

func PingErrorHandle(c *gin.Context) {
     api.SetStatus(1400).SetErrorf("A Bad Request").Apply(c)
     return
}

the api respone should be

# curl localhost:8080/ping
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Feb 2020 09:52:01 GMT
Content-Length: 32

{"message":"pong","status":1200}

# curl -i localhost:8080/pingerror
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Sun, 02 Feb 2020 09:58:27 GMT
Content-Length: 41

{"status":1400,"err_msg":"A Bad Request"}
Database

Management of database connection config is one of my original intentions to develop xin,but it's not very ideal for now。

I failed to design a proper interface that can adapt all sql driver

Let's take xorm which is used default by xin as an example to introduce how to use xin.Config for database connection management

Notice: following example need build tag usexorm : go run -tags=usexorm example.go

Assume we have two postgresql connection foo and bar in following config.toml

[db]
    driver = "postgres"
    enable_log = true
    [db.foo]
        source = "host=foo.postgresql.url port=5432 user=FOODBADMIN dbname=FOODB password=FOOPASSWD sslmode=disable"
    [db.bar]
        source = "host=bar.postgresql.url port=5432 user=BARDBADMIN dbname=BARDB password=BARPASSWD sslmode=disable"

Use xorm sql service

package main

import (
    "github.com/xieqiaoyu/xin"
    db "github.com/xieqiaoyu/xin/db/sql"
)
func main() {
    // init config
    configLoader := xin.NewFileConfigLoader("config.toml", "toml")
    config := xin.NewConfig(configLoader, nil)
    config.Init()
  
    // init xorm service
    xorm := db.NewXormService(config)
    // singleton load xorm engine of connection foo 
    fooDBEngine,err := xorm.Engine("foo")
    if err != nil {
        panic(err)
    }
    // fooDBEngine is an opened xorm.Engine 
    err = fooDBEngine.Ping()
    if err != nil {
        panic(err)
    }
    /*
    
    // singleton load xorm engine of connection bar
    barDBEngine,err := xorm.Engine("bar")
    if err != nil {
        panic(err)
    }
    var sliceOfStructs []Struct
    err := barDBEngine.Find(&sliceOfStructs)
    
    */
    
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewConfigTestCmd

func NewConfigTestCmd(config VerifiableConfig) *cobra.Command

NewConfigTestCmd return a cobra command for configtest command: configtest

func NewTracedE

func NewTracedE(err error) error

NewTracedE create a new TracedError and wrap the given error into it

func NewTracedEf

func NewTracedEf(format string, a ...interface{}) error

NewTracedEf create an error by format string and wrap it into a new TracedError

func NewVersionCmd

func NewVersionCmd(version string) *cobra.Command

NewVersionCmd return a cobra command to print giving version string command: version

func NewWrapEf

func NewWrapEf(format string, a ...interface{}) error

NewWrapEf @deprecated use NewTracedEf instead create an error by format string and wrap it into a new TracedError

func WaitForQuitSignal

func WaitForQuitSignal()

WaitForQuitSignal block until get a quit signal (SIGINT,SIGTERM) ,use for graceful stop

func WrapE

func WrapE(Err WrapError, err error) error

WrapE wrap the given into the given WrapError

func WrapEf

func WrapEf(Err WrapError, format string, a ...interface{}) error

WrapEf create an error by format string and wrap it into the given WrapError

Types

type Config

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

Config Config

func NewConfig

func NewConfig(configloader ConfigLoader, configVerifier ConfigVerifier) *Config

NewConfig create a new config

func (*Config) EnableDbLog

func (c *Config) EnableDbLog() bool

EnableDbLog get config for enable db Log

func (*Config) Env

func (c *Config) Env() string

Env get env string

func (*Config) GetMongoURI added in v0.5.3

func (c *Config) GetMongoURI(id string) (mongoURI string, err error)

GetMongoURI get mongo connect string

func (*Config) GetRedisURI

func (c *Config) GetRedisURI(id string) (redisURI string, err error)

GetRedisURI get redis connect string

func (*Config) GetSQLSource

func (c *Config) GetSQLSource(id string) (driver string, source string, err error)

GetSQLSource get driver and source string for sql connection

func (*Config) GrpcListen

func (c *Config) GrpcListen() (network, address string)

GrpcListen get grpc listen info

func (*Config) HTTPListen

func (c *Config) HTTPListen() string

HTTPListen get http listen string

func (*Config) Init

func (c *Config) Init() error

Init init config,load config and verfiy ,this method must be called before other method

func (*Config) Verify

func (c *Config) Verify() error

Verify verify config

func (*Config) Viper

func (c *Config) Viper() *viper.Viper

Viper Get viper instance of config

type ConfigLoader

type ConfigLoader interface {
	LoadConfig(vc *viper.Viper) error
}

ConfigLoader load config interface

type ConfigVerifier

type ConfigVerifier interface {
	VerfiyConfig(vc *viper.Viper) error
}

ConfigVerifier verify config interface

type EnvConfig

type EnvConfig interface {
	//get working mode string
	Env() string
}

EnvConfig a config interface for envirment

type EnvSetting

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

EnvSetting implement Envirment interface

func NewEnvSetting

func NewEnvSetting(config EnvConfig) *EnvSetting

NewEnvSetting generate new EnvSetting

func (*EnvSetting) Mode

func (e *EnvSetting) Mode() Mode

Mode return app working mode

type Envirment

type Envirment interface {
	//Return working mode
	Mode() Mode
}

Envirment envirment interface

type FileConfigLoader

type FileConfigLoader struct {
	FileName   string
	ConfigType string
}

FileConfigLoader load config from file system

func NewFileConfigLoader

func NewFileConfigLoader(filename, configType string) *FileConfigLoader

NewFileConfigLoader create a new file config loader

func (*FileConfigLoader) LoadConfig

func (l *FileConfigLoader) LoadConfig(vc *viper.Viper) error

LoadConfig ConfigLoader implement

type InternalError

type InternalError struct{ TracedError }

InternalError an error for framework internal use

type JSONSchemaConfigVerifier

type JSONSchemaConfigVerifier struct {
	Schema string
}

JSONSchemaConfigVerifier verfiy config by jsonschema

func NewJSONSchemaConfigVerifier

func NewJSONSchemaConfigVerifier(schema string) *JSONSchemaConfigVerifier

NewJSONSchemaConfigVerifier create a new jsonschema config verifier

func (JSONSchemaConfigVerifier) VerfiyConfig

func (jv JSONSchemaConfigVerifier) VerfiyConfig(vc *viper.Viper) error

VerfiyConfig ConfigVerifier interface

type Mode

type Mode int

Mode app mode

const (
	DevMode Mode = iota
	TestMode
	ReleaseMode
)

Predefine mode const

type StringConfigLoader

type StringConfigLoader struct {
	Str        string
	ConfigType string
}

StringConfigLoader load config from string

func NewStringConfigLoader

func NewStringConfigLoader(str, configType string) *StringConfigLoader

NewStringConfigLoader NewStringConfigLoader

func (*StringConfigLoader) LoadConfig

func (l *StringConfigLoader) LoadConfig(vc *viper.Viper) error

LoadConfig ConfigLoader implement

type TracedError

type TracedError struct {
	File string
	Line int
	Err  error
}

TracedError a WrapError store the position where error occur

func (*TracedError) Error

func (e *TracedError) Error() string

Error error interface

func (*TracedError) Unwrap

func (e *TracedError) Unwrap() error

Unwrap use for errors.Unwrap

func (*TracedError) Wrap

func (e *TracedError) Wrap(err error)

Wrap WrapError interface,do not call this function directly , use WrapE func instead

type VerifiableConfig

type VerifiableConfig interface {
	Verify() error
}

VerifiableConfig a config interface for config test

type WrapError

type WrapError interface {
	error
	//Wrap Wrap a error in current error
	Wrap(error)
}

WrapError WrapError

Directories

Path Synopsis
db
sql
api
Package api 提供api 相关的一些定义和工具函数
Package api 提供api 相关的一些定义和工具函数
api/middleware
Package middleware 自定义 api 中间键包,提供常用业务场景中会使用到的一些中间键封装 某些自定义功能可能会需要几个中间键组合在一起才能生效
Package middleware 自定义 api 中间键包,提供常用业务场景中会使用到的一些中间键封装 某些自定义功能可能会需要几个中间键组合在一起才能生效
util
curl
Package curl 封装go 标准http 库使其支持类似curl 的调用
Package curl 封装go 标准http 库使其支持类似curl 的调用
jsonschema
Package jsonschema 提供利用 jsonschema 来进行json 验证的一些工具函数
Package jsonschema 提供利用 jsonschema 来进行json 验证的一些工具函数
random
Package random provides tools for some random string
Package random provides tools for some random string

Jump to

Keyboard shortcuts

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