cli: allow customization of server api paths (close #4016) (#4174)

This commit is contained in:
Aravind Shankar 2020-04-08 16:29:21 +05:30 committed by GitHub
parent b02cd336d0
commit 21b61af109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 28 deletions

View File

@ -11,6 +11,7 @@ The order and collapsed state of columns is now persisted across page navigation
### Bug fixes and improvements
- cli: allow customization of server api paths (close #4016)
- cli: clean up migration files created during a failed migrate api (close #4312) (#4319)
- cli: add support for multiple versions of plugin (close #4105)
- cli: template assets path in console HTML for unversioned builds

View File

@ -12,7 +12,9 @@ import (
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
@ -61,6 +63,36 @@ const (
V2
)
// ServerAPIPaths has the custom paths defined for server api
type ServerAPIPaths struct {
Query string `yaml:"query,omitempty"`
GraphQL string `yaml:"graphql,omitempty"`
Config string `yaml:"config,omitempty"`
PGDump string `yaml:"pg_dump,omitempty"`
Version string `yaml:"version,omitempty"`
}
// GetQueryParams - encodes the values in url
func (s ServerAPIPaths) GetQueryParams() url.Values {
vals := url.Values{}
t := reflect.TypeOf(s)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag.Get("yaml")
splitTag := strings.Split(tag, ",")
if len(splitTag) == 0 {
continue
}
name := splitTag[0]
if name == "-" {
continue
}
v := reflect.ValueOf(s).Field(i)
vals.Add(name, v.String())
}
return vals
}
// ErrInvalidConfigVersion - if the config version is not valid
var ErrInvalidConfigVersion error = fmt.Errorf("invalid config version")
@ -105,10 +137,19 @@ type ServerConfig struct {
AccessKey string `yaml:"access_key,omitempty"`
// AdminSecret (optional) Admin secret required to query the endpoint
AdminSecret string `yaml:"admin_secret,omitempty"`
// APIPaths (optional) API paths for server
APIPaths *ServerAPIPaths `yaml:"api_paths,omitempty"`
ParsedEndpoint *url.URL `yaml:"-"`
}
// GetVersionEndpoint provides the url to contact the version API
func (s *ServerConfig) GetVersionEndpoint() string {
nurl := *s.ParsedEndpoint
nurl.Path = path.Join(nurl.Path, s.APIPaths.Version)
return nurl.String()
}
// ParseEndpoint ensures the endpoint is valid.
func (s *ServerConfig) ParseEndpoint() error {
nurl, err := url.Parse(s.Endpoint)
@ -398,7 +439,7 @@ func (ec *ExecutionContext) Validate() error {
}
func (ec *ExecutionContext) checkServerVersion() error {
v, err := version.FetchServerVersion(ec.Config.ServerConfig.Endpoint)
v, err := version.FetchServerVersion(ec.Config.ServerConfig.GetVersionEndpoint())
if err != nil {
return errors.Wrap(err, "failed to get version from server")
}
@ -441,6 +482,11 @@ func (ec *ExecutionContext) readConfig() error {
v.SetDefault("endpoint", "http://localhost:8080")
v.SetDefault("admin_secret", "")
v.SetDefault("access_key", "")
v.SetDefault("api_paths.query", "v1/query")
v.SetDefault("api_paths.graphql", "v1/graphql")
v.SetDefault("api_paths.config", "v1alpha1/config")
v.SetDefault("api_paths.pg_dump", "v1alpha1/pg_dump")
v.SetDefault("api_paths.version", "v1/version")
v.SetDefault("metadata_directory", "")
v.SetDefault("migrations_directory", "migrations")
v.SetDefault("actions.kind", "synchronous")
@ -462,6 +508,13 @@ func (ec *ExecutionContext) readConfig() error {
ServerConfig: ServerConfig{
Endpoint: v.GetString("endpoint"),
AdminSecret: adminSecret,
APIPaths: &ServerAPIPaths{
Query: v.GetString("api_paths.query"),
GraphQL: v.GetString("api_paths.graphql"),
Config: v.GetString("api_paths.config"),
PGDump: v.GetString("api_paths.pg_dump"),
Version: v.GetString("api_paths.version"),
},
},
MetadataDirectory: v.GetString("metadata_directory"),
MigrationsDirectory: v.GetString("migrations_directory"),

View File

@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/hasura/graphql-engine/cli"
"github.com/hasura/graphql-engine/cli/metadata/actions/types"
"github.com/hasura/graphql-engine/cli/migrate/database/hasuradb"
"github.com/hasura/graphql-engine/cli/migrate/source"
"github.com/hasura/graphql-engine/cli/migrate/source/file"
@ -319,7 +320,23 @@ func newScriptsUpdateConfigV2Cmd(ec *cli.ExecutionContext) *cobra.Command {
return errors.Wrap(err, "cannot write metadata")
}
ec.Spin("Writing new config file...")
err = ec.WriteConfig(nil)
// Read the config from config.yaml
cfgByt, err := ioutil.ReadFile(ec.ConfigFile)
if err != nil {
return errors.Wrap(err, "cannot read config file")
}
var cfg cli.Config
err = yaml.Unmarshal(cfgByt, &cfg)
if err != nil {
return errors.Wrap(err, "cannot parse config file")
}
cfg.Version = cli.V2
cfg.MetadataDirectory = ec.Viper.GetString("metadata_directory")
cfg.ActionConfig = &types.ActionExecutionConfig{
Kind: ec.Viper.GetString("actions.kind"),
HandlerWebhookBaseURL: ec.Viper.GetString("actions.handler_webhook_baseurl"),
}
err = ec.WriteConfig(&cfg)
if err != nil {
return errors.Wrap(err, "cannot write config file")
}

View File

@ -40,9 +40,9 @@ var (
type Config struct {
MigrationsTable string
SettingsTable string
v1URL *nurl.URL
queryURL *nurl.URL
graphqlURL *nurl.URL
schemDumpURL *nurl.URL
pgDumpURL *nurl.URL
Headers map[string]string
isCMD bool
Plugins types.MetadataPlugins
@ -115,20 +115,20 @@ func (h *HasuraDB) Open(url string, isCMD bool, logger *log.Logger) (database.Dr
config := &Config{
MigrationsTable: DefaultMigrationsTable,
SettingsTable: DefaultSettingsTable,
v1URL: &nurl.URL{
queryURL: &nurl.URL{
Scheme: scheme,
Host: hurl.Host,
Path: path.Join(hurl.Path, "v1/query"),
Path: path.Join(hurl.Path, params.Get("query")),
},
graphqlURL: &nurl.URL{
Scheme: scheme,
Host: hurl.Host,
Path: path.Join(hurl.Path, "v1/graphql"),
Path: path.Join(hurl.Path, params.Get("graphql")),
},
schemDumpURL: &nurl.URL{
pgDumpURL: &nurl.URL{
Scheme: scheme,
Host: hurl.Host,
Path: path.Join(hurl.Path, "v1alpha1/pg_dump"),
Path: path.Join(hurl.Path, params.Get("pg_dump")),
},
isCMD: isCMD,
Headers: headers,
@ -417,7 +417,7 @@ func (h *HasuraDB) ensureVersionTable() error {
func (h *HasuraDB) sendv1Query(m interface{}) (resp *http.Response, body []byte, err error) {
request := gorequest.New()
request = request.Post(h.config.v1URL.String()).Send(m)
request = request.Post(h.config.queryURL.String()).Send(m)
for headerName, headerValue := range h.config.Headers {
request.Set(headerName, headerValue)
}
@ -455,7 +455,7 @@ func (h *HasuraDB) sendv1GraphQL(query interface{}) (resp *http.Response, body [
func (h *HasuraDB) sendSchemaDumpQuery(m interface{}) (resp *http.Response, body []byte, err error) {
request := gorequest.New()
request = request.Post(h.config.schemDumpURL.String()).Send(m)
request = request.Post(h.config.pgDumpURL.String()).Send(m)
for headerName, headerValue := range h.config.Headers {
request.Set(headerName, headerValue)

View File

@ -121,7 +121,7 @@ func FilterCustomQuery(u *nurl.URL) *nurl.URL {
}
func NewMigrate(ec *cli.ExecutionContext, isCmd bool) (*Migrate, error) {
dbURL := GetDataPath(ec.Config.ServerConfig.ParsedEndpoint, GetAdminSecretHeaderName(ec.Version), ec.Config.ServerConfig.AdminSecret)
dbURL := GetDataPath(ec)
fileURL := GetFilePath(ec.MigrationDir)
t, err := New(fileURL.String(), dbURL.String(), isCmd, int(ec.Config.Version), ec.Logger)
if err != nil {
@ -132,13 +132,15 @@ func NewMigrate(ec *cli.ExecutionContext, isCmd bool) (*Migrate, error) {
return t, nil
}
func GetDataPath(url *nurl.URL, adminSecretHeader, adminSecretValue string) *nurl.URL {
func GetDataPath(ec *cli.ExecutionContext) *nurl.URL {
url := ec.Config.ServerConfig.ParsedEndpoint
host := &nurl.URL{
Scheme: "hasuradb",
Host: url.Host,
Path: url.Path,
RawQuery: ec.Config.ServerConfig.APIPaths.GetQueryParams().Encode(),
}
q := url.Query()
q := host.Query()
// Set sslmode in query
switch scheme := url.Scheme; scheme {
case "https":
@ -146,8 +148,8 @@ func GetDataPath(url *nurl.URL, adminSecretHeader, adminSecretValue string) *nur
default:
q.Set("sslmode", "disable")
}
if adminSecretValue != "" {
q.Add("headers", fmt.Sprintf("%s:%s", adminSecretHeader, adminSecretValue))
if GetAdminSecretHeaderName(ec.Version) != "" {
q.Add("headers", fmt.Sprintf("%s:%s", GetAdminSecretHeaderName(ec.Version), ec.Config.ServerConfig.AdminSecret))
}
host.RawQuery = q.Encode()
return host

View File

@ -1,10 +1,8 @@
package version
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
yaml "github.com/ghodss/yaml"
"github.com/pkg/errors"
@ -16,12 +14,7 @@ type serverVersionResponse struct {
// FetchServerVersion reads the version from server.
func FetchServerVersion(endpoint string) (version string, err error) {
ep, err := url.Parse(endpoint)
if err != nil {
return "", errors.Wrap(err, "cannot parse endpoint as a valid url")
}
versionEndpoint := fmt.Sprintf("%s/v1/version", ep.String())
response, err := http.Get(versionEndpoint)
response, err := http.Get(endpoint)
if err != nil {
return "", errors.Wrap(err, "failed making version api call")
}
@ -30,7 +23,7 @@ func FetchServerVersion(endpoint string) (version string, err error) {
case http.StatusNotFound:
return "", nil
default:
return "", errors.Errorf("GET %s failed - [%d]", versionEndpoint, response.StatusCode)
return "", errors.Errorf("GET %s failed - [%d]", endpoint, response.StatusCode)
}
} else {
defer response.Body.Close()