2018-06-24 16:40:48 +03:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-04-23 06:10:24 +03:00
|
|
|
"net/url"
|
2018-06-24 16:40:48 +03:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2020-06-16 12:08:36 +03:00
|
|
|
crontriggers "github.com/hasura/graphql-engine/cli/metadata/cron_triggers"
|
|
|
|
|
2020-02-24 19:14:46 +03:00
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/actions"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/actions/types"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/allowlist"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/functions"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/querycollections"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/remoteschemas"
|
|
|
|
"github.com/hasura/graphql-engine/cli/metadata/tables"
|
|
|
|
metadataTypes "github.com/hasura/graphql-engine/cli/metadata/types"
|
|
|
|
metadataVersion "github.com/hasura/graphql-engine/cli/metadata/version"
|
|
|
|
"github.com/hasura/graphql-engine/cli/util"
|
|
|
|
|
2018-06-24 16:40:48 +03:00
|
|
|
"github.com/hasura/graphql-engine/cli"
|
|
|
|
"github.com/manifoldco/promptui"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/spf13/cobra"
|
2019-01-28 16:55:28 +03:00
|
|
|
"github.com/spf13/viper"
|
2018-06-24 16:40:48 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-02-24 19:14:46 +03:00
|
|
|
defaultDirectory string = "hasura"
|
2018-06-24 16:40:48 +03:00
|
|
|
)
|
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
// NewInitCmd is the definition for init command
|
2018-06-24 16:40:48 +03:00
|
|
|
func NewInitCmd(ec *cli.ExecutionContext) *cobra.Command {
|
2020-02-24 19:14:46 +03:00
|
|
|
opts := &InitOptions{
|
2018-06-24 16:40:48 +03:00
|
|
|
EC: ec,
|
|
|
|
}
|
|
|
|
initCmd := &cobra.Command{
|
2019-12-25 11:31:29 +03:00
|
|
|
Use: "init [directory-name]",
|
2018-08-06 14:33:17 +03:00
|
|
|
Short: "Initialize directory for Hasura GraphQL Engine migrations",
|
|
|
|
Long: "Create directories and files required for enabling migrations on Hasura GraphQL Engine",
|
|
|
|
Example: ` # Create a directory to store migrations
|
2019-12-25 11:31:29 +03:00
|
|
|
hasura init [directory-name]
|
2018-06-24 16:40:48 +03:00
|
|
|
|
2019-02-14 12:37:47 +03:00
|
|
|
# Now, edit <my-directory>/config.yaml to add endpoint and admin secret
|
2018-06-24 16:47:01 +03:00
|
|
|
|
2019-02-14 12:37:47 +03:00
|
|
|
# Create a directory with endpoint and admin secret configured:
|
2019-12-25 11:31:29 +03:00
|
|
|
hasura init <my-project> --endpoint https://my-graphql-engine.com --admin-secret adminsecretkey
|
2018-08-06 14:33:17 +03:00
|
|
|
|
2020-04-27 07:33:10 +03:00
|
|
|
# Create a hasura project in the current working directory
|
|
|
|
hasura init .
|
|
|
|
|
2020-02-27 13:13:07 +03:00
|
|
|
# See https://hasura.io/docs/1.0/graphql/manual/migrations/index.html for more details`,
|
2018-06-24 16:40:48 +03:00
|
|
|
SilenceUsage: true,
|
2019-12-25 11:31:29 +03:00
|
|
|
Args: cobra.MaximumNArgs(1),
|
2020-02-24 19:14:46 +03:00
|
|
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
2019-01-28 16:55:28 +03:00
|
|
|
ec.Viper = viper.New()
|
2020-02-24 19:14:46 +03:00
|
|
|
err := ec.Prepare()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return ec.PluginsConfig.Repo.EnsureCloned()
|
2018-06-27 15:04:09 +03:00
|
|
|
},
|
2018-06-24 16:40:48 +03:00
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2019-12-25 11:31:29 +03:00
|
|
|
if len(args) == 1 {
|
|
|
|
opts.InitDir = args[0]
|
|
|
|
}
|
2020-02-24 19:14:46 +03:00
|
|
|
return opts.Run()
|
2018-06-24 16:40:48 +03:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
f := initCmd.Flags()
|
2020-03-26 06:24:05 +03:00
|
|
|
f.Var(cli.NewConfigVersionValue(cli.V2, &opts.Version), "version", "config version to be used")
|
2018-06-24 16:40:48 +03:00
|
|
|
f.StringVar(&opts.InitDir, "directory", "", "name of directory where files will be created")
|
|
|
|
f.StringVar(&opts.Endpoint, "endpoint", "", "http(s) endpoint for Hasura GraphQL Engine")
|
2019-02-14 12:37:47 +03:00
|
|
|
f.StringVar(&opts.AdminSecret, "admin-secret", "", "admin secret for Hasura GraphQL Engine")
|
|
|
|
f.StringVar(&opts.AdminSecret, "access-key", "", "access key for Hasura GraphQL Engine")
|
2020-02-24 19:14:46 +03:00
|
|
|
f.StringVar(&opts.Template, "install-manifest", "", "install manifest to be cloned")
|
2019-02-14 12:37:47 +03:00
|
|
|
f.MarkDeprecated("access-key", "use --admin-secret instead")
|
2019-12-25 11:31:29 +03:00
|
|
|
f.MarkDeprecated("directory", "use directory-name argument instead")
|
2019-02-14 12:37:47 +03:00
|
|
|
|
2018-06-24 16:40:48 +03:00
|
|
|
return initCmd
|
|
|
|
}
|
|
|
|
|
2020-02-24 19:14:46 +03:00
|
|
|
type InitOptions struct {
|
2018-06-24 16:40:48 +03:00
|
|
|
EC *cli.ExecutionContext
|
|
|
|
|
2020-03-26 06:24:05 +03:00
|
|
|
Version cli.ConfigVersion
|
2019-02-14 12:37:47 +03:00
|
|
|
Endpoint string
|
|
|
|
AdminSecret string
|
|
|
|
InitDir string
|
2020-02-24 19:14:46 +03:00
|
|
|
|
|
|
|
Template string
|
2018-06-24 16:40:48 +03:00
|
|
|
}
|
|
|
|
|
2020-02-24 19:14:46 +03:00
|
|
|
func (o *InitOptions) Run() error {
|
2020-04-26 19:54:48 +03:00
|
|
|
var infoMsg string
|
2020-04-27 05:49:26 +03:00
|
|
|
// prompt for init directory if it's not set already
|
|
|
|
if o.InitDir == "" {
|
|
|
|
p := promptui.Prompt{
|
|
|
|
Label: "Name of project directory ",
|
|
|
|
Default: defaultDirectory,
|
|
|
|
}
|
|
|
|
r, err := p.Run()
|
|
|
|
if err != nil {
|
|
|
|
return handlePromptError(err)
|
|
|
|
}
|
|
|
|
if strings.TrimSpace(r) != "" {
|
|
|
|
o.InitDir = r
|
|
|
|
} else {
|
|
|
|
o.InitDir = defaultDirectory
|
|
|
|
}
|
|
|
|
}
|
2020-04-27 10:56:34 +03:00
|
|
|
|
|
|
|
cwdir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error getting current working directory")
|
|
|
|
}
|
|
|
|
initPath, err := filepath.Abs(o.InitDir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if initPath == cwdir {
|
2020-04-27 05:49:26 +03:00
|
|
|
// check if pwd is filesystem root
|
2020-04-27 10:56:34 +03:00
|
|
|
if err := cli.CheckFilesystemBoundary(cwdir); err != nil {
|
2020-04-27 05:49:26 +03:00
|
|
|
return errors.Wrap(err, "can't initialise hasura project in filesystem root")
|
|
|
|
}
|
|
|
|
// check if the current directory is already a hasura project
|
2020-04-27 10:56:34 +03:00
|
|
|
if err := cli.ValidateDirectory(cwdir); err == nil {
|
2020-04-27 05:49:26 +03:00
|
|
|
return errors.Errorf("current working directory is already a hasura project directory")
|
2018-06-24 16:40:48 +03:00
|
|
|
}
|
2020-04-27 10:56:34 +03:00
|
|
|
o.EC.ExecutionDirectory = cwdir
|
2020-10-29 08:09:36 +03:00
|
|
|
infoMsg = `hasura project initialised. execute the following command to continue:
|
2020-04-26 19:54:48 +03:00
|
|
|
hasura console
|
2020-10-29 08:09:36 +03:00
|
|
|
`
|
2018-09-27 16:57:17 +03:00
|
|
|
} else {
|
2020-04-26 19:54:48 +03:00
|
|
|
// create execution directory
|
|
|
|
err := o.createExecutionDirectory()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
infoMsg = fmt.Sprintf(`directory created. execute the following commands to continue:
|
2018-06-24 16:40:48 +03:00
|
|
|
|
|
|
|
cd %s
|
2018-08-06 14:33:17 +03:00
|
|
|
hasura console
|
|
|
|
`, o.EC.ExecutionDirectory)
|
2020-04-26 19:54:48 +03:00
|
|
|
}
|
2018-06-24 16:40:48 +03:00
|
|
|
|
2020-02-24 19:14:46 +03:00
|
|
|
// create template files
|
2020-04-27 10:56:34 +03:00
|
|
|
err = o.createTemplateFiles()
|
2020-02-24 19:14:46 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
// create other required files, like config.yaml, migrations directory
|
2018-08-06 14:33:17 +03:00
|
|
|
err = o.createFiles()
|
2018-06-24 16:40:48 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
o.EC.Logger.Info(infoMsg)
|
2018-06-24 16:40:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-26 19:54:48 +03:00
|
|
|
//create the execution directory
|
|
|
|
func (o *InitOptions) createExecutionDirectory() error {
|
|
|
|
if o.EC.ExecutionDirectory == "" {
|
|
|
|
o.EC.ExecutionDirectory = o.InitDir
|
|
|
|
} else {
|
|
|
|
o.EC.ExecutionDirectory = filepath.Join(o.EC.ExecutionDirectory, o.InitDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the execution directory
|
|
|
|
if _, err := os.Stat(o.EC.ExecutionDirectory); err == nil {
|
|
|
|
return errors.Errorf("directory '%s' already exist", o.EC.ExecutionDirectory)
|
|
|
|
}
|
|
|
|
err := os.MkdirAll(o.EC.ExecutionDirectory, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating setup directories")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
// createFiles creates files required by the CLI in the ExecutionDirectory
|
2020-02-24 19:14:46 +03:00
|
|
|
func (o *InitOptions) createFiles() error {
|
2018-06-27 15:04:09 +03:00
|
|
|
// create the directory
|
2018-06-24 16:40:48 +03:00
|
|
|
err := os.MkdirAll(filepath.Dir(o.EC.ExecutionDirectory), os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating setup directories")
|
|
|
|
}
|
2018-06-27 15:04:09 +03:00
|
|
|
// set config object
|
2020-02-24 19:14:46 +03:00
|
|
|
var config *cli.Config
|
2020-03-26 06:24:05 +03:00
|
|
|
if o.Version == cli.V1 {
|
2020-02-24 19:14:46 +03:00
|
|
|
config = &cli.Config{
|
|
|
|
ServerConfig: cli.ServerConfig{
|
|
|
|
Endpoint: "http://localhost:8080",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
config = &cli.Config{
|
2020-03-26 06:24:05 +03:00
|
|
|
Version: o.Version,
|
2020-02-24 19:14:46 +03:00
|
|
|
ServerConfig: cli.ServerConfig{
|
|
|
|
Endpoint: "http://localhost:8080",
|
|
|
|
},
|
2020-04-25 09:03:13 +03:00
|
|
|
MetadataDirectory: "metadata",
|
2020-03-26 06:24:05 +03:00
|
|
|
ActionConfig: &types.ActionExecutionConfig{
|
2020-04-25 09:03:13 +03:00
|
|
|
Kind: "synchronous",
|
|
|
|
HandlerWebhookBaseURL: "http://localhost:3000",
|
2020-02-24 19:14:46 +03:00
|
|
|
},
|
|
|
|
}
|
2018-06-24 16:40:48 +03:00
|
|
|
}
|
2018-06-27 15:04:09 +03:00
|
|
|
if o.Endpoint != "" {
|
2020-04-23 06:10:24 +03:00
|
|
|
if _, err := url.ParseRequestURI(o.Endpoint); err != nil {
|
|
|
|
return errors.Wrap(err, "error validating endpoint URL")
|
|
|
|
}
|
2020-02-24 19:14:46 +03:00
|
|
|
config.ServerConfig.Endpoint = o.Endpoint
|
2018-06-24 16:40:48 +03:00
|
|
|
}
|
2019-02-14 12:37:47 +03:00
|
|
|
if o.AdminSecret != "" {
|
2020-02-24 19:14:46 +03:00
|
|
|
config.ServerConfig.AdminSecret = o.AdminSecret
|
2018-06-27 15:04:09 +03:00
|
|
|
}
|
2018-06-24 16:40:48 +03:00
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
// write the config file
|
2020-02-24 19:14:46 +03:00
|
|
|
o.EC.Config = config
|
2018-06-27 15:04:09 +03:00
|
|
|
o.EC.ConfigFile = filepath.Join(o.EC.ExecutionDirectory, "config.yaml")
|
2020-02-24 19:14:46 +03:00
|
|
|
err = o.EC.WriteConfig(nil)
|
2018-06-24 16:40:48 +03:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot write config file")
|
|
|
|
}
|
|
|
|
|
2018-06-27 15:04:09 +03:00
|
|
|
// create migrations directory
|
2020-06-16 15:15:04 +03:00
|
|
|
o.EC.MigrationDir = filepath.Join(o.EC.ExecutionDirectory, cli.DefaultMigrationsDirectory)
|
2018-06-24 16:40:48 +03:00
|
|
|
err = os.MkdirAll(o.EC.MigrationDir, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot write migration directory")
|
|
|
|
}
|
|
|
|
|
2020-02-24 19:14:46 +03:00
|
|
|
if config.Version == cli.V2 {
|
|
|
|
// create metadata directory
|
2020-06-16 15:15:04 +03:00
|
|
|
o.EC.MetadataDir = filepath.Join(o.EC.ExecutionDirectory, cli.DefaultMetadataDirectory)
|
2020-02-24 19:14:46 +03:00
|
|
|
err = os.MkdirAll(o.EC.MetadataDir, os.ModePerm)
|
|
|
|
if err != nil {
|
2020-11-24 09:11:30 +03:00
|
|
|
return errors.Wrap(err, "cannot write metadata directory")
|
2020-02-24 19:14:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// create metadata files
|
|
|
|
plugins := make(metadataTypes.MetadataPlugins, 0)
|
|
|
|
plugins = append(plugins, metadataVersion.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, tables.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, functions.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, querycollections.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, allowlist.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, remoteschemas.New(o.EC, o.EC.MetadataDir))
|
|
|
|
plugins = append(plugins, actions.New(o.EC, o.EC.MetadataDir))
|
2020-06-16 12:08:36 +03:00
|
|
|
plugins = append(plugins, crontriggers.New(o.EC, o.EC.MetadataDir))
|
2020-02-24 19:14:46 +03:00
|
|
|
for _, plg := range plugins {
|
|
|
|
err := plg.CreateFiles()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot create metadata files")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-16 15:15:04 +03:00
|
|
|
|
|
|
|
// create seeds directory
|
|
|
|
o.EC.SeedsDirectory = filepath.Join(o.EC.ExecutionDirectory, cli.DefaultSeedsDirectory)
|
|
|
|
err = os.MkdirAll(o.EC.SeedsDirectory, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "cannot write seeds directory")
|
|
|
|
}
|
2020-02-24 19:14:46 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *InitOptions) createTemplateFiles() error {
|
|
|
|
if o.Template == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
err := o.EC.InitTemplatesRepo.EnsureUpdated()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error in updating init-templates repo")
|
|
|
|
}
|
|
|
|
templatePath := filepath.Join(o.EC.InitTemplatesRepo.Path, o.Template)
|
|
|
|
info, err := os.Stat(templatePath)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "template doesn't exists")
|
|
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
|
|
return errors.Errorf("template should be a directory")
|
|
|
|
}
|
|
|
|
err = util.CopyDir(templatePath, filepath.Join(o.EC.ExecutionDirectory, "install-manifest"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-06-24 16:40:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func handlePromptError(err error) error {
|
|
|
|
if err == promptui.ErrInterrupt {
|
|
|
|
return errors.New("cancelled by user")
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "prompt failed")
|
|
|
|
}
|