super-graph/serv/cmd.go

299 lines
6.6 KiB
Go

package serv
import (
"context"
"fmt"
"os"
"strings"
"github.com/dosco/super-graph/psql"
"github.com/dosco/super-graph/qcode"
"github.com/gobuffalo/flect"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/log/zerologadapter"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
//go:generate esc -o static.go -ignore \\.DS_Store -prefix ../web/build -private -pkg serv ../web/build
const (
serverName = "Super Graph"
authFailBlockAlways = iota + 1
authFailBlockPerQuery
authFailBlockNever
)
var (
logger *zerolog.Logger
conf *config
confPath string
db *pgxpool.Pool
qcompile *qcode.Compiler
pcompile *psql.Compiler
authFailBlock int
rootCmd *cobra.Command
servCmd *cobra.Command
seedCmd *cobra.Command
migrateCmd *cobra.Command
statusCmd *cobra.Command
newMigrationCmd *cobra.Command
)
func Init() {
var err error
rootCmd = &cobra.Command{
Use: "super-graph",
Short: "An instant high-performance GraphQL API. No code needed. https://supergraph.dev",
//Run: cmdServ,
}
seedCmd = &cobra.Command{
Use: "seed",
Short: "Run the seed script to seed the database",
Run: cmdSeed,
}
servCmd = &cobra.Command{
Use: "serv",
Short: "Run the super-graph service",
Run: cmdServ,
}
migrateCmd = &cobra.Command{
Use: "migrate",
Short: "Migrate the database",
Long: `Migrate the database to destination migration version.
Destination migration version can be one of the following value types:
An integer:
Migrate to a specific migration.
e.g. tern migrate -d 42
"+" and an integer:
Migrate forward N steps.
e.g. tern migrate -d +3
"-" and an integer:
Migrate backward N steps.
e.g. tern migrate -d -2
"-+" and an integer:
Redo previous N steps (migrate backward N steps then forward N steps).
e.g. tern migrate -d -+1
The word "last":
Migrate to the most recent migration. This is the default value, so it is
never needed to specify directly.
e.g. tern migrate
e.g. tern migrate -d last
`,
Run: cmdMigrate,
}
statusCmd = &cobra.Command{
Use: "status",
Short: "Print current migration status",
Run: cmdStatus,
}
newMigrationCmd = &cobra.Command{
Use: "new NAME",
Short: "Generate a new migration",
Long: "Generate a new migration with the next sequence number and provided name",
Run: cmdNewMigration,
}
logger = initLog()
rootCmd.Flags().StringVar(&confPath,
"path", "./config", "path to config files")
//cmdMigrate.Flags().StringVarP(&cliOptions.destinationVersion,
// "destination", "d", "last", "destination migration version")
rootCmd.AddCommand(servCmd)
rootCmd.AddCommand(seedCmd)
rootCmd.AddCommand(migrateCmd)
rootCmd.AddCommand(statusCmd)
rootCmd.AddCommand(newMigrationCmd)
if conf, err = initConf(); err != nil {
logger.Fatal().Err(err).Msg("failed to read config")
}
if err := rootCmd.Execute(); err != nil {
logger.Fatal().Err(err).Send()
}
}
func initLog() *zerolog.Logger {
logger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).
With().
Timestamp().
Caller().
Logger()
return &logger
}
func initConf() (*config, error) {
vi := viper.New()
vi.SetEnvPrefix("SG")
vi.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
vi.AutomaticEnv()
vi.AddConfigPath(confPath)
vi.AddConfigPath("./config")
vi.SetConfigName(getConfigName())
vi.SetDefault("host_port", "0.0.0.0:8080")
vi.SetDefault("web_ui", false)
vi.SetDefault("enable_tracing", false)
vi.SetDefault("auth_fail_block", "always")
vi.SetDefault("seed_file", "seed.js")
vi.SetDefault("database.type", "postgres")
vi.SetDefault("database.host", "localhost")
vi.SetDefault("database.port", 5432)
vi.SetDefault("database.user", "postgres")
vi.SetDefault("database.schema", "public")
vi.SetDefault("env", "development")
vi.BindEnv("env", "GO_ENV")
vi.BindEnv("HOST", "HOST")
vi.BindEnv("PORT", "PORT")
vi.SetDefault("auth.rails.max_idle", 80)
vi.SetDefault("auth.rails.max_active", 12000)
if err := vi.ReadInConfig(); err != nil {
return nil, err
}
c := &config{}
if err := vi.Unmarshal(c); err != nil {
return nil, fmt.Errorf("unable to decode config, %v", err)
}
for k, v := range c.Inflections {
flect.AddPlural(k, v)
}
for i := range c.DB.Tables {
t := c.DB.Tables[i]
t.Name = flect.Pluralize(strings.ToLower(t.Name))
}
authFailBlock = getAuthFailBlock(c)
logLevel, err := zerolog.ParseLevel(c.LogLevel)
if err != nil {
logger.Error().Err(err).Msg("error setting log_level")
}
zerolog.SetGlobalLevel(logLevel)
//fmt.Printf("%#v", c)
return c, nil
}
func initDB(c *config) (*pgx.Conn, error) {
config, _ := pgx.ParseConfig("")
config.Host = c.DB.Host
config.Port = c.DB.Port
config.Database = c.DB.DBName
config.User = c.DB.User
config.Password = c.DB.Password
config.RuntimeParams = map[string]string{
"application_name": c.AppName,
"search_path": c.DB.Schema,
}
switch c.LogLevel {
case "debug":
config.LogLevel = pgx.LogLevelDebug
case "info":
config.LogLevel = pgx.LogLevelInfo
case "warn":
config.LogLevel = pgx.LogLevelWarn
case "error":
config.LogLevel = pgx.LogLevelError
default:
config.LogLevel = pgx.LogLevelNone
}
config.Logger = zerologadapter.NewLogger(*logger)
db, err := pgx.ConnectConfig(context.Background(), config)
if err != nil {
return nil, err
}
return db, nil
}
func initDBPool(c *config) (*pgxpool.Pool, error) {
config, _ := pgxpool.ParseConfig("")
config.ConnConfig.Host = c.DB.Host
config.ConnConfig.Port = c.DB.Port
config.ConnConfig.Database = c.DB.DBName
config.ConnConfig.User = c.DB.User
config.ConnConfig.Password = c.DB.Password
config.ConnConfig.RuntimeParams = map[string]string{
"application_name": c.AppName,
"search_path": c.DB.Schema,
}
switch c.LogLevel {
case "debug":
config.ConnConfig.LogLevel = pgx.LogLevelDebug
case "info":
config.ConnConfig.LogLevel = pgx.LogLevelInfo
case "warn":
config.ConnConfig.LogLevel = pgx.LogLevelWarn
case "error":
config.ConnConfig.LogLevel = pgx.LogLevelError
default:
config.ConnConfig.LogLevel = pgx.LogLevelNone
}
config.ConnConfig.Logger = zerologadapter.NewLogger(*logger)
// if c.DB.MaxRetries != 0 {
// opt.MaxRetries = c.DB.MaxRetries
// }
if c.DB.PoolSize != 0 {
config.MaxConns = conf.DB.PoolSize
}
db, err := pgxpool.ConnectConfig(context.Background(), config)
if err != nil {
return nil, err
}
return db, nil
}
func initCompiler() {
var err error
qcompile, pcompile, err = initCompilers(conf)
if err != nil {
logger.Fatal().Err(err).Msg("failed to initialize compilers")
}
if err := initResolvers(); err != nil {
logger.Fatal().Err(err).Msg("failed to initialized resolvers")
}
}