Mermaid is a tool helping user use Cobra, Viper and dig together.
Mermaid bind flags from cobra to viper as settings. And provide all settings to dig container automatically. Make it easy to setup and write testing.
-
config
: config file path or filename.yaml -
config_name
: ifconfig
field is blank, mermaid will find config yaml file in.
,/
,$HOME
,./config
. Default name isconfig
. -
log_level
: Set logger's log level. Default is info level.
cmd/example/main.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/jneo8/mermaid"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.uber.org/dig"
"os"
)
type Service interface {
Run() error
}
type HttpService struct {
Engine *gin.Engine
Username string
}
func (s *HttpService) Run() error {
s.Engine.GET("ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
s.Engine.GET("user", func(c *gin.Context) {
c.JSON(200, gin.H{"message": s.Username})
})
return s.Engine.Run()
}
type ServiceOptions struct {
dig.In
Username string `name:"username"`
}
func NewHttpService(opts ServiceOptions) Service {
log.Infof("New HpptService %v", opts)
return &HttpService{
Engine: gin.Default(),
Username: opts.Username,
}
}
var cmd = &cobra.Command{
Use: "exmaple",
Short: "exmaple",
RunE: func(cmd *cobra.Command, args []string) error {
initializers := []interface{}{
NewHttpService,
}
runable := func(service Service, logger *log.Logger, config *viper.Viper) error {
logger.Infof("Config: %v", config.AllSettings())
if err := service.Run(); err != nil {
logger.Error(err)
}
return nil
}
return mermaid.Run(cmd, runable, initializers...)
},
}
func init() {
cmd.Flags().String("username", "userA", "username")
}
func main() {
if err := cmd.Execute(); err != nil {
log.Error(err)
os.Exit(1)
}
}
cmd/example/main_test.go
package main
import (
"encoding/json"
"github.com/stretchr/testify/assert"
"net/http"
"sync"
"testing"
"time"
)
func TestRootCMD(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
// Overwrite config using args.
rootCMD.SetArgs(
[]string{"--username", "userB"},
)
if err := rootCMD.Execute(); err != nil {
t.Error(err)
}
}()
time.Sleep(1 * time.Second) // Waiting for gin engineer setup.
resp, err := http.Get("http://127.0.0.1:8080/user")
if err != nil {
t.Error(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
assert.Equal(t, result["message"], "userB")
}
cmd/customized_mermaid/main.go
package main
import (
"github.com/jneo8/mermaid"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func main() {
worker, err := mermaid.NewMermaid(
&cobra.Command{},
viper.New(),
log.New(),
"",
)
if err != nil {
log.Fatal(err)
}
runable := func(logger *log.Logger) error {
logger.Info("Customized mermaid worker")
return nil
}
initializers := []interface{}{}
if err := worker.Execute(runable, initializers...); err != nil {
log.Fatal(err)
}
}
The basic idea isn't from me. Is from here 結構化的 Go 專案設計模式與風格 — 4 - getamis - Medium.