diff --git a/provider/migrator.go b/provider/migrator.go index d00ad34b..8f5ecedc 100644 --- a/provider/migrator.go +++ b/provider/migrator.go @@ -2,8 +2,6 @@ package provider import ( "context" - "errors" - "fmt" "reflect" "runtime" "strconv" @@ -22,26 +20,16 @@ const queryTableColumns = `SELECT array_agg(column_name::text) as columns FROM i const addColumnToTable = `ALTER TABLE %s ADD COLUMN IF NOT EXISTS %v %v;` -const maxTableName = 63 - -const maxColumnName = 63 - // Migrator handles creation of schema.Table in database if they don't exist type Migrator struct { - db schema.Database - log hclog.Logger - validators []TableValidator + db schema.Database + log hclog.Logger } func NewMigrator(db schema.Database, log hclog.Logger) Migrator { return Migrator{ db, log, - []TableValidator{ - LengthTableValidator{ - log: log, - }, - }, } } @@ -124,33 +112,3 @@ func (m Migrator) buildColumns(ctb *sqlbuilder.CreateTableBuilder, cc []schema.C ctb.Define(defs...) } } - -func (m Migrator) ValidateTable(t *schema.Table) error { - for _, validator := range m.validators { - return validator.Validate(t) - } - return nil -} - -type TableValidator interface { - Validate(t *schema.Table) error -} - -type LengthTableValidator struct { - log hclog.Logger -} - -func (tv LengthTableValidator) Validate(t *schema.Table) error { - if len(t.Name) > maxTableName { - tv.log.Error("table name has exceeded max length", "table", t.Name) - return errors.New("table name has exceeded max length") - } - - for _, col := range t.ColumnNames() { - if len(col) > maxColumnName { - tv.log.Error("column name has exceeded max length", "column", col) - return errors.New(fmt.Sprintf("column name %s has exceeded max length", col)) - } - } - return nil -} diff --git a/provider/migrator_test.go b/provider/migrator_test.go deleted file mode 100644 index fd45ea32..00000000 --- a/provider/migrator_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package provider - -import ( - "github.com/cloudquery/cq-provider-sdk/logging" - "github.com/cloudquery/cq-provider-sdk/provider/schema" - "github.com/hashicorp/go-hclog" - "github.com/stretchr/testify/assert" - "testing" -) - -var testTable = schema.Table{ - Name: "test_table_validator", - Columns: []schema.Column{ - { - Name: "zero_bool", - Type: schema.TypeBool, - }, - { - Name: "zero_int", - Type: schema.TypeBigInt, - }, - { - Name: "not_zero_bool", - Type: schema.TypeBool, - }, - }, -} - -func TestMigratorTableValidators(t *testing.T) { - logger := logging.New(&hclog.LoggerOptions{ - Level: hclog.Trace, - JSONFormat: true, - }) - m := NewMigrator(nil, logger) - - // table has passed all validators - err := m.ValidateTable(&testTable) - assert.Nil(t, err) - - // table name is too long - tableWithLongName := testTable - tableWithLongName.Name = "WithLongNametableWithLongNametableWithLongNametableWithLongNamet" - err = m.ValidateTable(&tableWithLongName) - assert.Error(t, err) - - // column name is too long - tableWithLongColumnName := testTable - tableWithLongName.Columns[0].Name = "tableWithLongColumnNametableWithLongColumnNametableWithLongColumnName" - err = m.ValidateTable(&tableWithLongColumnName) - assert.Error(t, err) -} diff --git a/provider/provider.go b/provider/provider.go index 5a435e4c..d1a28869 100644 --- a/provider/provider.go +++ b/provider/provider.go @@ -62,7 +62,7 @@ func (p *Provider) Init(_ string, dsn string, _ bool) error { for _, t := range p.ResourceMap { // validate table - validationErr := m.ValidateTable(t) + validationErr := schema.ValidateTable(t) if validationErr != nil { p.Logger.Error("table validation failed", "table", t.Name, "error", err) return err diff --git a/provider/schema/resolvers_test.go b/provider/schema/resolvers_test.go index 0aa4131d..5a3aa5de 100644 --- a/provider/schema/resolvers_test.go +++ b/provider/schema/resolvers_test.go @@ -24,6 +24,22 @@ func TestPathResolver(t *testing.T) { resource := &Resource{ Item: testStruct{Inner: innerStruct{Value: "bla"}, Value: 5, unexported: false}, data: map[string]interface{}{}, + table: &Table{ + Columns: []Column{ + { + Name: "test", + Type: TypeString, + }, + { + Name: "int_value", + Type: TypeInt, + }, + { + Name: "unexported", + Type: TypeBool, + }, + }, + }, } err := r1(context.TODO(), nil, resource, Column{Name: "test"}) diff --git a/provider/schema/resource.go b/provider/schema/resource.go index 622d9b89..00290969 100644 --- a/provider/schema/resource.go +++ b/provider/schema/resource.go @@ -1,6 +1,7 @@ package schema import ( + "errors" "fmt" "github.com/cloudquery/go-funk" "github.com/google/uuid" @@ -33,12 +34,13 @@ func (r Resource) Get(key string) interface{} { return r.data[key] } -func (r Resource) Set(key string, value interface{}) { +func (r Resource) Set(key string, value interface{}) error { columnExists := funk.ContainsString(r.table.ColumnNames(), key) if !columnExists { - panic(fmt.Sprintf("column %s does not exits", key)) + return errors.New(fmt.Sprintf("column %s does not exits", key)) } r.data[key] = value + return nil } func (r Resource) Id() uuid.UUID { diff --git a/provider/schema/resources_test.go b/provider/schema/resources_test.go index 2d6f41d9..39134de6 100644 --- a/provider/schema/resources_test.go +++ b/provider/schema/resources_test.go @@ -77,7 +77,8 @@ func TestResourceColumns(t *testing.T) { assert.Equal(t, v, []interface{}{r.id, "test", "name_no_prefix", "prefix_name"}) // check non existing col - assert.Panics(t, func() { r.Set("non_exist_col", "test") }) + err = r.Set("non_exist_col", "test") + assert.Error(t, err) } func TestResourceResolveColumns(t *testing.T) { diff --git a/provider/schema/validators.go b/provider/schema/validators.go new file mode 100644 index 00000000..e6ffa559 --- /dev/null +++ b/provider/schema/validators.go @@ -0,0 +1,54 @@ +package schema + +import ( + "errors" + "fmt" +) + +const maxTableName = 63 + +const maxColumnName = 63 + +var defaultValidators = []TableValidator{ + LengthTableValidator{}, +} + +func ValidateTable(t *Table) error { + for _, validator := range defaultValidators { + return validator.Validate(t) + } + return nil +} + +type TableValidator interface { + Validate(t *Table) error +} + +type LengthTableValidator struct{} + +func validateTableAttributesNameLength(t *Table) error { + // validate table name + if len(t.Name) > maxTableName { + return errors.New("table name has exceeded max length") + } + + // validate table columns + for _, col := range t.ColumnNames() { + if len(col) > maxColumnName { + return errors.New(fmt.Sprintf("column name %s has exceeded max length", col)) + } + } + + // validate table relations + for _, rel := range t.Relations { + err := validateTableAttributesNameLength(rel) + if err != nil { + return err + } + } + return nil +} + +func (tv LengthTableValidator) Validate(t *Table) error { + return validateTableAttributesNameLength(t) +} diff --git a/provider/schema/validators_test.go b/provider/schema/validators_test.go new file mode 100644 index 00000000..f2010504 --- /dev/null +++ b/provider/schema/validators_test.go @@ -0,0 +1,42 @@ +package schema + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +var testTableValidators = Table{ + Name: "test_table_validator", + Columns: []Column{ + { + Name: "zero_bool", + Type: TypeBool, + }, + { + Name: "zero_int", + Type: TypeBigInt, + }, + { + Name: "not_zero_bool", + Type: TypeBool, + }, + }, +} + +func TestTableValidators(t *testing.T) { + // table has passed all validators + err := ValidateTable(&testTableValidators) + assert.Nil(t, err) + + // table name is too long + tableWithLongName := testTableValidators + tableWithLongName.Name = "WithLongNametableWithLongNametableWithLongNametableWithLongNamet" + err = ValidateTable(&tableWithLongName) + assert.Error(t, err) + + // column name is too long + tableWithLongColumnName := testTableValidators + tableWithLongName.Columns[0].Name = "tableWithLongColumnNametableWithLongColumnNametableWithLongColumnName" + err = ValidateTable(&tableWithLongColumnName) + assert.Error(t, err) +}