mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-18 19:05:19 -05:00
chore: return error for untested fields in tools.yaml (#239)
This only checks within `SourceConfig`, `ToolConfig`, and `AuthSourceConfig`. Error when an unknown field is provided: `2025-01-27T22:43:46.988401-08:00 ERROR "unable to parse tool file at \"tools.yaml\": unable to parse as \"cloud-sql-postgres\": [2:1] unknown field \"extra\"\n 1 | database: test_database\n> 2 | extra: here\n ^\n 3 | instance: toolbox-cloudsql\n 4 | kind: cloud-sql-postgres\n 5 | password: postgres\n 6 | "` Error when a required field is not provided: `2025-01-27T17:49:47.584846-08:00 ERROR "unable to parse tool file at \"tools.yaml\": validation failed: Key: 'Config.Region' Error:Field validation for 'Region' failed on the 'required' tag"` --------- Co-authored-by: Kurtis Van Gent <31518063+kurtisvg@users.noreply.github.com>
This commit is contained in:
@@ -30,9 +30,9 @@ var _ auth.AuthSourceConfig = Config{}
|
||||
|
||||
// Auth source configuration
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
ClientID string `yaml:"clientId"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
ClientID string `yaml:"clientId" validate:"required"`
|
||||
}
|
||||
|
||||
// Returns the auth source kind
|
||||
|
||||
@@ -135,76 +135,84 @@ func (c *SourceConfigs) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
}
|
||||
|
||||
for name, u := range raw {
|
||||
var k struct {
|
||||
Kind string `yaml:"kind"`
|
||||
// Unmarshal to a general type that ensure it capture all fields
|
||||
var v map[string]any
|
||||
if err := u.Unmarshal(&v); err != nil {
|
||||
return fmt.Errorf("unable to unmarshal %q: %w", name, err)
|
||||
}
|
||||
err := u.Unmarshal(&k)
|
||||
|
||||
kind, ok := v["kind"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing 'kind' field for %q", name)
|
||||
}
|
||||
|
||||
dec, err := util.NewStrictDecoder(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing 'kind' field for %q", k)
|
||||
return fmt.Errorf("error creating decoder: %w", err)
|
||||
}
|
||||
switch k.Kind {
|
||||
switch kind {
|
||||
case alloydbpgsrc.SourceKind:
|
||||
actual := alloydbpgsrc.Config{Name: name, IPType: "public"}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case cloudsqlpgsrc.SourceKind:
|
||||
actual := cloudsqlpgsrc.Config{Name: name, IPType: "public"}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case postgressrc.SourceKind:
|
||||
actual := postgressrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case cloudsqlmysqlsrc.SourceKind:
|
||||
actual := cloudsqlmysqlsrc.Config{Name: name, IPType: "public"}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case mysqlsrc.SourceKind:
|
||||
actual := mysqlsrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case spannersrc.SourceKind:
|
||||
actual := spannersrc.Config{Name: name, Dialect: "googlesql"}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case neo4jrc.SourceKind:
|
||||
actual := neo4jrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case cloudsqlmssqlsrc.SourceKind:
|
||||
actual := cloudsqlmssqlsrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
actual := cloudsqlmssqlsrc.Config{Name: name, IPType: "public"}
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case mssqlsrc.SourceKind:
|
||||
actual := mssqlsrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case dgraphsrc.SourceKind:
|
||||
actual := dgraphsrc.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
default:
|
||||
return fmt.Errorf("%q is not a valid kind of data source", k.Kind)
|
||||
return fmt.Errorf("%q is not a valid kind of data source", kind)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -226,22 +234,29 @@ func (c *AuthSourceConfigs) UnmarshalYAML(unmarshal func(interface{}) error) err
|
||||
}
|
||||
|
||||
for name, u := range raw {
|
||||
var k struct {
|
||||
Kind string `yaml:"kind"`
|
||||
var v map[string]any
|
||||
if err := u.Unmarshal(&v); err != nil {
|
||||
return fmt.Errorf("unable to unmarshal %q: %w", name, err)
|
||||
}
|
||||
err := u.Unmarshal(&k)
|
||||
|
||||
kind, ok := v["kind"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing 'kind' field for %q", name)
|
||||
}
|
||||
|
||||
dec, err := util.NewStrictDecoder(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing 'kind' field for %q", k)
|
||||
return fmt.Errorf("error creating decoder: %w", err)
|
||||
}
|
||||
switch k.Kind {
|
||||
switch kind {
|
||||
case google.AuthSourceKind:
|
||||
actual := google.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
default:
|
||||
return fmt.Errorf("%q is not a valid kind of auth source", k.Kind)
|
||||
return fmt.Errorf("%q is not a valid kind of auth source", kind)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -262,52 +277,59 @@ func (c *ToolConfigs) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
}
|
||||
|
||||
for name, u := range raw {
|
||||
var k struct {
|
||||
Kind string `yaml:"kind"`
|
||||
var v map[string]any
|
||||
if err := u.Unmarshal(&v); err != nil {
|
||||
return fmt.Errorf("unable to unmarshal %q: %w", name, err)
|
||||
}
|
||||
err := u.Unmarshal(&k)
|
||||
if err != nil {
|
||||
|
||||
kind, ok := v["kind"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing 'kind' field for %q", name)
|
||||
}
|
||||
switch k.Kind {
|
||||
|
||||
dec, err := util.NewStrictDecoder(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating decoder: %w", err)
|
||||
}
|
||||
switch kind {
|
||||
case postgressql.ToolKind:
|
||||
actual := postgressql.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case mysqlsql.ToolKind:
|
||||
actual := mysqlsql.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case spanner.ToolKind:
|
||||
actual := spanner.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case neo4jtool.ToolKind:
|
||||
actual := neo4jtool.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case mssqlsql.ToolKind:
|
||||
actual := mssqlsql.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
case dgraph.ToolKind:
|
||||
actual := dgraph.Config{Name: name}
|
||||
if err := u.Unmarshal(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", k.Kind, err)
|
||||
if err := dec.Decode(&actual); err != nil {
|
||||
return fmt.Errorf("unable to parse as %q: %w", kind, err)
|
||||
}
|
||||
(*c)[name] = actual
|
||||
default:
|
||||
return fmt.Errorf("%q is not a valid kind of tool", k.Kind)
|
||||
return fmt.Errorf("%q is not a valid kind of tool", kind)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ const SourceKind string = "alloydb-postgres"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
Region string `yaml:"region"`
|
||||
Cluster string `yaml:"cluster"`
|
||||
Instance string `yaml:"instance"`
|
||||
IPType sources.IPType `yaml:"ipType"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Project string `yaml:"project" validate:"required"`
|
||||
Region string `yaml:"region" validate:"required"`
|
||||
Cluster string `yaml:"cluster" validate:"required"`
|
||||
Instance string `yaml:"instance" validate:"required"`
|
||||
IPType sources.IPType `yaml:"ipType" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -42,6 +42,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
cluster: my-cluster
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: map[string]sources.SourceConfig{
|
||||
"my-pg-instance": alloydbpg.Config{
|
||||
@@ -53,6 +55,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,6 +72,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: Public
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: map[string]sources.SourceConfig{
|
||||
"my-pg-instance": alloydbpg.Config{
|
||||
@@ -79,6 +85,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -94,6 +102,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: private
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: map[string]sources.SourceConfig{
|
||||
"my-pg-instance": alloydbpg.Config{
|
||||
@@ -105,6 +115,8 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "private",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -126,10 +138,11 @@ func TestParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func FailParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "invalid ipType",
|
||||
@@ -143,7 +156,42 @@ func FailParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: fail
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"alloydb-postgres\": ipType invalid: must be one of \"public\", or \"private\"",
|
||||
},
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: alloydb-postgres
|
||||
project: my-project
|
||||
region: my-region
|
||||
cluster: my-cluster
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"alloydb-postgres\": [3:1] unknown field \"foo\"\n 1 | cluster: my-cluster\n 2 | database: my_db\n> 3 | foo: bar\n ^\n 4 | instance: my-instance\n 5 | kind: alloydb-postgres\n 6 | password: my_pass\n 7 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: alloydb-postgres
|
||||
region: my-region
|
||||
cluster: my-cluster
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"alloydb-postgres\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
@@ -154,7 +202,11 @@ func FailParseFromYamlAlloyDBPg(t *testing.T) {
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail: %s", err)
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,16 +33,16 @@ var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
// Cloud SQL MSSQL configs
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
Region string `yaml:"region"`
|
||||
Instance string `yaml:"instance"`
|
||||
IPAddress string `yaml:"ipAddress"`
|
||||
IPType string `yaml:"ipType"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Project string `yaml:"project" validate:"required"`
|
||||
Region string `yaml:"region" validate:"required"`
|
||||
Instance string `yaml:"instance" validate:"required"`
|
||||
IPAddress string `yaml:"ipAddress" validate:"required"`
|
||||
IPType sources.IPType `yaml:"ipType" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
@@ -52,7 +52,7 @@ func (r Config) SourceConfigKind() string {
|
||||
|
||||
func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
|
||||
// Initializes a Cloud SQL MSSQL source
|
||||
db, err := initCloudSQLMssqlConnection(ctx, tracer, r.Name, r.Project, r.Region, r.Instance, r.IPAddress, r.IPType, r.User, r.Password, r.Database)
|
||||
db, err := initCloudSQLMssqlConnection(ctx, tracer, r.Name, r.Project, r.Region, r.Instance, r.IPAddress, r.IPType.String(), r.User, r.Password, r.Database)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create db connection: %w", err)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ func TestParseFromYamlCloudSQLMssql(t *testing.T) {
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
ipAddress: localhost
|
||||
ipType: public
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-instance": cloudsqlmssql.Config{
|
||||
@@ -53,6 +54,8 @@ func TestParseFromYamlCloudSQLMssql(t *testing.T) {
|
||||
IPAddress: "localhost",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,3 +77,77 @@ func TestParseFromYamlCloudSQLMssql(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "invalid ipType",
|
||||
in: `
|
||||
sources:
|
||||
my-instance:
|
||||
kind: cloud-sql-mssql
|
||||
project: my-project
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
ipType: fail
|
||||
database: my_db
|
||||
ipAddress: localhost
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mssql\": ipType invalid: must be one of \"public\", or \"private\"",
|
||||
},
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-instance:
|
||||
kind: cloud-sql-mssql
|
||||
project: my-project
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
ipAddress: localhost
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mssql\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | instance: my-instance\n 4 | ipAddress: localhost\n 5 | kind: cloud-sql-mssql\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-instance:
|
||||
kind: cloud-sql-mssql
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
ipAddress: localhost
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mssql\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,15 @@ const SourceKind string = "cloud-sql-mysql"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
Region string `yaml:"region"`
|
||||
Instance string `yaml:"instance"`
|
||||
IPType sources.IPType `yaml:"ipType"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Project string `yaml:"project" validate:"required"`
|
||||
Region string `yaml:"region" validate:"required"`
|
||||
Instance string `yaml:"instance" validate:"required"`
|
||||
IPType sources.IPType `yaml:"ipType" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -40,6 +40,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-mysql-instance": cloudsqlmysql.Config{
|
||||
@@ -50,6 +52,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,6 +68,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: Public
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-mysql-instance": cloudsqlmysql.Config{
|
||||
@@ -74,6 +80,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -88,6 +96,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: private
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-mysql-instance": cloudsqlmysql.Config{
|
||||
@@ -98,6 +108,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "private",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -120,10 +132,11 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func FailParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "invalid ipType",
|
||||
@@ -136,7 +149,40 @@ func FailParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: fail
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mysql\": ipType invalid: must be one of \"public\", or \"private\"",
|
||||
},
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-mysql-instance:
|
||||
kind: cloud-sql-mysql
|
||||
project: my-project
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mysql\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | instance: my-instance\n 4 | kind: cloud-sql-mysql\n 5 | password: my_pass\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-mysql-instance:
|
||||
kind: cloud-sql-mysql
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-mysql\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
@@ -147,7 +193,11 @@ func FailParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail: %s", err)
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,15 +32,15 @@ const SourceKind string = "cloud-sql-postgres"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
Region string `yaml:"region"`
|
||||
Instance string `yaml:"instance"`
|
||||
IPType sources.IPType `yaml:"ipType"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Project string `yaml:"project" validate:"required"`
|
||||
Region string `yaml:"region" validate:"required"`
|
||||
Instance string `yaml:"instance" validate:"required"`
|
||||
IPType sources.IPType `yaml:"ipType" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -40,6 +40,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-pg-instance": cloudsqlpg.Config{
|
||||
@@ -50,6 +52,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,6 +68,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: Public
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-pg-instance": cloudsqlpg.Config{
|
||||
@@ -74,6 +80,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "public",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -88,6 +96,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: private
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-pg-instance": cloudsqlpg.Config{
|
||||
@@ -98,6 +108,8 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
Instance: "my-instance",
|
||||
IPType: "private",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -120,10 +132,11 @@ func TestParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func FailParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "invalid ipType",
|
||||
@@ -136,7 +149,40 @@ func FailParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
instance: my-instance
|
||||
ipType: fail
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-postgres\": ipType invalid: must be one of \"public\", or \"private\"",
|
||||
},
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: cloud-sql-postgres
|
||||
project: my-project
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-postgres\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | instance: my-instance\n 4 | kind: cloud-sql-postgres\n 5 | password: my_pass\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: cloud-sql-postgres
|
||||
region: my-region
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"cloud-sql-postgres\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
@@ -147,7 +193,11 @@ func FailParseFromYamlCloudSQLPg(t *testing.T) {
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail: %s", err)
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ type DgraphClient struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
DgraphUrl string `yaml:"dgraphUrl"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
DgraphUrl string `yaml:"dgraphUrl" validate:"required"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Namespace uint64 `yaml:"namespace"`
|
||||
@@ -108,7 +108,7 @@ func initDgraphHttpClient(ctx context.Context, tracer trace.Tracer, r Config) (*
|
||||
|
||||
hc := &DgraphClient{
|
||||
httpClient: &http.Client{},
|
||||
baseUrl: r.DgraphUrl,
|
||||
baseUrl: r.DgraphUrl,
|
||||
HttpToken: &HttpToken{
|
||||
UserId: r.User,
|
||||
Namespace: r.Namespace,
|
||||
|
||||
@@ -54,6 +54,22 @@ func TestParseFromYamlDgraph(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "basic example minimal field",
|
||||
in: `
|
||||
sources:
|
||||
my-dgraph-instance:
|
||||
kind: dgraph
|
||||
dgraphUrl: https://localhost:8080
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-dgraph-instance": dgraph.Config{
|
||||
Name: "my-dgraph-instance",
|
||||
Kind: dgraph.SourceKind,
|
||||
DgraphUrl: "https://localhost:8080",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
@@ -74,3 +90,48 @@ func TestParseFromYamlDgraph(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-dgraph-instance:
|
||||
kind: dgraph
|
||||
dgraphUrl: https://localhost:8080
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"dgraph\": [2:1] unknown field \"foo\"\n 1 | dgraphUrl: https://localhost:8080\n> 2 | foo: bar\n ^\n 3 | kind: dgraph",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-dgraph-instance:
|
||||
kind: dgraph
|
||||
`,
|
||||
err: "unable to parse as \"dgraph\": Key: 'Config.DgraphUrl' Error:Field validation for 'DgraphUrl' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
// Cloud SQL MSSQL configs
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Host string `yaml:"host" validate:"required"`
|
||||
Port string `yaml:"port" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -39,6 +39,8 @@ func TestParseFromYamlMssql(t *testing.T) {
|
||||
host: 0.0.0.0
|
||||
port: my-port
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-mssql-instance": mssql.Config{
|
||||
@@ -47,6 +49,8 @@ func TestParseFromYamlMssql(t *testing.T) {
|
||||
Host: "0.0.0.0",
|
||||
Port: "my-port",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,3 +72,56 @@ func TestParseFromYamlMssql(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-mssql-instance:
|
||||
kind: mssql
|
||||
host: 0.0.0.0
|
||||
port: my-port
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"mssql\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | host: 0.0.0.0\n 4 | kind: mssql\n 5 | password: my_pass\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-mssql-instance:
|
||||
kind: mssql
|
||||
host: 0.0.0.0
|
||||
port: my-port
|
||||
database: my_db
|
||||
user: my_user
|
||||
`,
|
||||
err: "unable to parse as \"mssql\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ const SourceKind string = "mysql"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Host string `yaml:"host" validate:"required"`
|
||||
Port string `yaml:"port" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -39,6 +39,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
host: 0.0.0.0
|
||||
port: my-host
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-mysql-instance": mysql.Config{
|
||||
@@ -47,6 +49,8 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
Host: "0.0.0.0",
|
||||
Port: "my-host",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,3 +72,56 @@ func TestParseFromYamlCloudSQLMySQL(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-mysql-instance:
|
||||
kind: mysql
|
||||
host: 0.0.0.0
|
||||
port: my-host
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"mysql\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | host: 0.0.0.0\n 4 | kind: mysql\n 5 | password: my_pass\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-mysql-instance:
|
||||
kind: mysql
|
||||
port: my-host
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
err: "unable to parse as \"mysql\": Key: 'Config.Host' Error:Field validation for 'Host' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ const SourceKind string = "neo4j"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Uri string `yaml:"uri"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Uri string `yaml:"uri" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -38,6 +38,8 @@ func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
kind: neo4j
|
||||
uri: neo4j+s://my-host:7687
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-neo4j-instance": neo4j.Config{
|
||||
@@ -45,6 +47,8 @@ func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
Kind: neo4j.SourceKind,
|
||||
Uri: "neo4j+s://my-host:7687",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -66,3 +70,54 @@ func TestParseFromYamlNeo4j(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-neo4j-instance:
|
||||
kind: neo4j
|
||||
uri: neo4j+s://my-host:7687
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"neo4j\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | kind: neo4j\n 4 | password: my_pass\n 5 | uri: neo4j+s://my-host:7687\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-neo4j-instance:
|
||||
kind: neo4j
|
||||
uri: neo4j+s://my-host:7687
|
||||
database: my_db
|
||||
user: my_user
|
||||
`,
|
||||
err: "unable to parse as \"neo4j\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ const SourceKind string = "postgres"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Host string `yaml:"host"`
|
||||
Port string `yaml:"port"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Host string `yaml:"host" validate:"required"`
|
||||
Port string `yaml:"port" validate:"required"`
|
||||
User string `yaml:"user" validate:"required"`
|
||||
Password string `yaml:"password" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -39,6 +39,8 @@ func TestParseFromYamlPostgres(t *testing.T) {
|
||||
host: my-host
|
||||
port: 0.0.0.0
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
`,
|
||||
want: server.SourceConfigs{
|
||||
"my-pg-instance": postgres.Config{
|
||||
@@ -47,6 +49,8 @@ func TestParseFromYamlPostgres(t *testing.T) {
|
||||
Host: "my-host",
|
||||
Port: "0.0.0.0",
|
||||
Database: "my_db",
|
||||
User: "my_user",
|
||||
Password: "my_pass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,3 +72,56 @@ func TestParseFromYamlPostgres(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: postgres
|
||||
host: my-host
|
||||
port: 0.0.0.0
|
||||
database: my_db
|
||||
user: my_user
|
||||
password: my_pass
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"postgres\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | host: my-host\n 4 | kind: postgres\n 5 | password: my_pass\n 6 | ",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-pg-instance:
|
||||
kind: postgres
|
||||
host: my-host
|
||||
port: 0.0.0.0
|
||||
database: my_db
|
||||
user: my_user
|
||||
`,
|
||||
err: "unable to parse as \"postgres\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Sources server.SourceConfigs `yaml:"sources"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ const SourceKind string = "spanner"
|
||||
var _ sources.SourceConfig = Config{}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
Instance string `yaml:"instance"`
|
||||
Dialect sources.Dialect `yaml:"dialect"`
|
||||
Database string `yaml:"database"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Project string `yaml:"project" validate:"required"`
|
||||
Instance string `yaml:"instance" validate:"required"`
|
||||
Dialect sources.Dialect `yaml:"dialect" validate:"required"`
|
||||
Database string `yaml:"database" validate:"required"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigKind() string {
|
||||
|
||||
@@ -115,10 +115,11 @@ func TestParseFromYamlSpannerDb(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func FailParseFromYamlSpanner(t *testing.T) {
|
||||
func TestFailParseFromYaml(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
desc: "invalid dialect",
|
||||
@@ -128,9 +129,34 @@ func FailParseFromYamlSpanner(t *testing.T) {
|
||||
kind: spanner
|
||||
project: my-project
|
||||
instance: my-instance
|
||||
dialect: fail
|
||||
dialect: fail
|
||||
database: my_db
|
||||
`,
|
||||
err: "unable to parse as \"spanner\": dialect invalid: must be one of \"googlesql\", or \"postgresql\"",
|
||||
},
|
||||
{
|
||||
desc: "extra field",
|
||||
in: `
|
||||
sources:
|
||||
my-spanner-instance:
|
||||
kind: spanner
|
||||
project: my-project
|
||||
instance: my-instance
|
||||
database: my_db
|
||||
foo: bar
|
||||
`,
|
||||
err: "unable to parse as \"spanner\": [2:1] unknown field \"foo\"\n 1 | database: my_db\n> 2 | foo: bar\n ^\n 3 | instance: my-instance\n 4 | kind: spanner\n 5 | project: my-project",
|
||||
},
|
||||
{
|
||||
desc: "missing required field",
|
||||
in: `
|
||||
sources:
|
||||
my-spanner-instance:
|
||||
kind: spanner
|
||||
project: my-project
|
||||
instance: my-instance
|
||||
`,
|
||||
err: "unable to parse as \"spanner\": Key: 'Config.Database' Error:Field validation for 'Database' failed on the 'required' tag",
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
@@ -141,7 +167,11 @@ func FailParseFromYamlSpanner(t *testing.T) {
|
||||
// Parse contents
|
||||
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||
if err == nil {
|
||||
t.Fatalf("expect parsing to fail: %s", err)
|
||||
t.Fatalf("expect parsing to fail")
|
||||
}
|
||||
errStr := err.Error()
|
||||
if errStr != tc.err {
|
||||
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ var _ compatibleSource = &dgraph.Source{}
|
||||
var compatibleSources = [...]string{dgraph.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
IsQuery bool `yaml:"isQuery"`
|
||||
Timeout string `yaml:"timeout"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
|
||||
@@ -39,11 +39,11 @@ var _ compatibleSource = &mssql.Source{}
|
||||
var compatibleSources = [...]string{cloudsqlmssql.SourceKind, mssql.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
AuthRequired []string `yaml:"authRequired"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ var _ compatibleSource = &mysql.Source{}
|
||||
var compatibleSources = [...]string{cloudsqlmysql.SourceKind, mysql.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
AuthRequired []string `yaml:"authRequired"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ var _ compatibleSource = &neo4jsc.Source{}
|
||||
var compatibleSources = [...]string{neo4jsc.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
|
||||
@@ -84,16 +84,16 @@ func (p ParamValues) AsMapByOrderedKeys() map[string]interface{} {
|
||||
// Input: {"role": "admin", "$age": 30}
|
||||
// Output: {"$role": "admin", "$age": 30}
|
||||
func (p ParamValues) AsMapWithDollarPrefix() map[string]interface{} {
|
||||
params := make(map[string]interface{})
|
||||
params := make(map[string]interface{})
|
||||
|
||||
for _, param := range p {
|
||||
key := param.Name
|
||||
if !strings.HasPrefix(key, "$") {
|
||||
for _, param := range p {
|
||||
key := param.Name
|
||||
if !strings.HasPrefix(key, "$") {
|
||||
key = "$" + key
|
||||
}
|
||||
params[key] = param.Value
|
||||
}
|
||||
return params
|
||||
params[key] = param.Value
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func parseFromAuthSource(paramAuthSources []ParamAuthSource, claimsMap map[string]map[string]any) (any, error) {
|
||||
@@ -178,44 +178,50 @@ func (c *Parameters) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
// parseParamFromDelayedUnmarshaler is a helper function that is required to parse
|
||||
// parameters because there are multiple different types
|
||||
func parseParamFromDelayedUnmarshaler(u *util.DelayedUnmarshaler) (Parameter, error) {
|
||||
var p CommonParameter
|
||||
var p map[string]any
|
||||
err := u.Unmarshal(&p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parameter missing required fields: %w", err)
|
||||
return nil, fmt.Errorf("error parsing parameters: %w", err)
|
||||
}
|
||||
switch p.Type {
|
||||
|
||||
t, ok := p["type"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("parameter is missing 'type' field: %w", err)
|
||||
}
|
||||
|
||||
switch t {
|
||||
case typeString:
|
||||
a := &StringParameter{}
|
||||
if err := u.Unmarshal(a); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", p.Type, err)
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", t, err)
|
||||
}
|
||||
return a, nil
|
||||
case typeInt:
|
||||
a := &IntParameter{}
|
||||
if err := u.Unmarshal(a); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", p.Type, err)
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", t, err)
|
||||
}
|
||||
return a, nil
|
||||
case typeFloat:
|
||||
a := &FloatParameter{}
|
||||
if err := u.Unmarshal(a); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", p.Type, err)
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", t, err)
|
||||
}
|
||||
return a, nil
|
||||
case typeBool:
|
||||
a := &BooleanParameter{}
|
||||
if err := u.Unmarshal(a); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", p.Type, err)
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", t, err)
|
||||
}
|
||||
return a, nil
|
||||
case typeArray:
|
||||
a := &ArrayParameter{}
|
||||
if err := u.Unmarshal(a); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", p.Type, err)
|
||||
return nil, fmt.Errorf("unable to parse as %q: %w", t, err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%q is not valid type for a parameter!", p.Type)
|
||||
return nil, fmt.Errorf("%q is not valid type for a parameter!", t)
|
||||
}
|
||||
|
||||
func (ps Parameters) Manifest() []ParameterManifest {
|
||||
@@ -515,15 +521,14 @@ type ArrayParameter struct {
|
||||
}
|
||||
|
||||
func (p *ArrayParameter) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
if err := unmarshal(&p.CommonParameter); err != nil {
|
||||
return err
|
||||
}
|
||||
var rawItem struct {
|
||||
Items util.DelayedUnmarshaler `yaml:"items"`
|
||||
CommonParameter `yaml:",inline"`
|
||||
Items util.DelayedUnmarshaler `yaml:"items"`
|
||||
}
|
||||
if err := unmarshal(&rawItem); err != nil {
|
||||
return err
|
||||
}
|
||||
p.CommonParameter = rawItem.CommonParameter
|
||||
i, err := parseParamFromDelayedUnmarshaler(&rawItem.Items)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse 'items' field: %w", err)
|
||||
|
||||
@@ -608,12 +608,12 @@ func TestAuthParametersParse(t *testing.T) {
|
||||
|
||||
func TestParamValues(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
in tools.ParamValues
|
||||
wantSlice []any
|
||||
wantMap map[string]interface{}
|
||||
wantMapOrdered map[string]interface{}
|
||||
wantMapWithDollar map[string]interface{}
|
||||
name string
|
||||
in tools.ParamValues
|
||||
wantSlice []any
|
||||
wantMap map[string]interface{}
|
||||
wantMapOrdered map[string]interface{}
|
||||
wantMapWithDollar map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "string",
|
||||
|
||||
@@ -41,11 +41,11 @@ var _ compatibleSource = &postgres.Source{}
|
||||
var compatibleSources = [...]string{alloydbpg.SourceKind, cloudsqlpg.SourceKind, postgres.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
AuthRequired []string `yaml:"authRequired"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ var _ compatibleSource = &spannerdb.Source{}
|
||||
var compatibleSources = [...]string{spannerdb.SourceKind}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Source string `yaml:"source"`
|
||||
Description string `yaml:"description"`
|
||||
Statement string `yaml:"statement"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description" validate:"required"`
|
||||
Statement string `yaml:"statement" validate:"required"`
|
||||
AuthRequired []string `yaml:"authRequired"`
|
||||
Parameters tools.Parameters `yaml:"parameters"`
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
)
|
||||
|
||||
@@ -39,3 +43,17 @@ type contextKey string
|
||||
|
||||
// UserAgentKey is the key used to store userAgent within context
|
||||
const UserAgentKey contextKey = "userAgent"
|
||||
|
||||
func NewStrictDecoder(v interface{}) (*yaml.Decoder, error) {
|
||||
b, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to marshal %q: %w", v, err)
|
||||
}
|
||||
|
||||
dec := yaml.NewDecoder(
|
||||
bytes.NewReader(b),
|
||||
yaml.Strict(),
|
||||
yaml.Validator(validator.New()),
|
||||
)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user