diff --git a/pkg/datasources/alerts.go b/pkg/datasources/alerts.go index 19e695c277..7572d102a6 100644 --- a/pkg/datasources/alerts.go +++ b/pkg/datasources/alerts.go @@ -87,24 +87,36 @@ func ReadAlerts(d *schema.ResourceData, meta interface{}) error { ctx := context.Background() d.SetId("alerts_read") - databaseName := d.Get("database").(string) - schemaName := d.Get("schema").(string) - alertPattern := d.Get("pattern").(string) - var like sdk.Like - if alertPattern != "" { - like = sdk.Like{Pattern: &alertPattern} + + opts := sdk.ShowAlertOptions{} + + if v, ok := d.GetOk("pattern"); ok { + alertPattern := v.(string) + opts.Like = &sdk.Like{Pattern: &alertPattern} } - listAlerts, err := client.Alerts.Show(ctx, &sdk.ShowAlertOptions{ - In: &sdk.In{ - Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName), - }, - Like: &like, - }) + + if v, ok := d.GetOk("database"); ok { + databaseName := v.(string) + + if v, ok := d.GetOk("schema"); ok { + schemaName := v.(string) + opts.In = &sdk.In{ + Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName), + } + } else { + opts.In = &sdk.In{ + Database: sdk.NewAccountObjectIdentifier(databaseName), + } + } + } + + listAlerts, err := client.Alerts.Show(ctx, &opts) if err != nil { log.Printf("[DEBUG] failed to list alerts in schema (%s)", d.Id()) d.SetId("") return err } + alerts := make([]map[string]any, 0, len(listAlerts)) for _, alert := range listAlerts { alertMap := map[string]any{} diff --git a/pkg/datasources/alerts_acceptance_test.go b/pkg/datasources/alerts_acceptance_test.go new file mode 100644 index 0000000000..a23dac149c --- /dev/null +++ b/pkg/datasources/alerts_acceptance_test.go @@ -0,0 +1,118 @@ +package datasources_test + +import ( + "fmt" + "strings" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_Alerts(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: alertsResourceConfig(name) + alertsDatasourceConfigNoOptionals(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_alerts.test_datasource_alert", "alerts.#"), + ), + }, + { + Config: alertsResourceConfig(name) + alertsDatasourceConfigDbOnly(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_alerts.test_datasource_alert", "alerts.#"), + ), + }, + { + Config: alertsResourceConfig(name) + alertsDatasourceConfigDbAndSchema(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_alerts.test_datasource_alert", "alerts.#"), + resource.TestCheckResourceAttr("data.snowflake_alerts.test_datasource_alert", "alerts.0.name", name), + ), + }, + { + Config: alertsResourceConfig(name) + alertsDatasourceConfigAllOptionals(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_alerts.test_datasource_alert", "alerts.#"), + resource.TestCheckResourceAttr("data.snowflake_alerts.test_datasource_alert", "alerts.0.name", name), + ), + }, + { + Config: alertsResourceConfig(name) + alertsDatasourceConfigSchemaOnly(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_alerts.test_datasource_alert", "alerts.#"), + ), + }, + }, + }) +} + +func alertsResourceConfig(name string) string { + return fmt.Sprintf(` +resource "snowflake_alert" "test_resource_alert" { + name = "%s" + database = "%s" + schema = "%s" + warehouse = "%s" + condition = "select 0 as c" + action = "select 0 as c" + enabled = false + comment = "some comment" + alert_schedule { + interval = "60" + } +} +`, name, acc.TestDatabaseName, acc.TestSchemaName, acc.TestWarehouseName) +} + +func alertsDatasourceConfigNoOptionals() string { + return ` +data "snowflake_alerts" "test_datasource_alert" {} +` +} + +func alertsDatasourceConfigDbOnly() string { + return fmt.Sprintf(` +data "snowflake_alerts" "test_datasource_alert" { + database = "%s" +} +`, acc.TestDatabaseName) +} + +func alertsDatasourceConfigDbAndSchema() string { + return fmt.Sprintf(` +data "snowflake_alerts" "test_datasource_alert" { + database = "%s" + schema = "%s" +} +`, acc.TestDatabaseName, acc.TestSchemaName) +} + +func alertsDatasourceConfigAllOptionals(name string) string { + return fmt.Sprintf(` +data "snowflake_alerts" "test_datasource_alert" { + database = "%s" + schema = "%s" + pattern = "%s" +} +`, acc.TestDatabaseName, acc.TestSchemaName, name) +} + +func alertsDatasourceConfigSchemaOnly() string { + return fmt.Sprintf(` +data "snowflake_alerts" "test_datasource_alert" { + schema = "%s" +} +`, acc.TestSchemaName) +} diff --git a/pkg/datasources/dynamic_tables.go b/pkg/datasources/dynamic_tables.go index e620204027..0093f5ea0b 100644 --- a/pkg/datasources/dynamic_tables.go +++ b/pkg/datasources/dynamic_tables.go @@ -33,22 +33,22 @@ var dynamicTablesSchema = map[string]*schema.Schema{ Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "account": { - Type: schema.TypeBool, - Optional: true, - Description: "Returns records for the entire account.", - ConflictsWith: []string{"in.database", "in.schema"}, + Type: schema.TypeBool, + Optional: true, + Description: "Returns records for the entire account.", + ExactlyOneOf: []string{"in.0.account", "in.0.database", "in.0.schema"}, }, "database": { - Type: schema.TypeString, - Optional: true, - Description: "Returns records for the current database in use or for a specified database (db_name).", - ConflictsWith: []string{"in.account", "in.schema"}, + Type: schema.TypeString, + Optional: true, + Description: "Returns records for the current database in use or for a specified database (db_name).", + ExactlyOneOf: []string{"in.0.account", "in.0.database", "in.0.schema"}, }, "schema": { - Type: schema.TypeString, - Optional: true, - Description: "Returns records for the current schema in use or a specified schema (schema_name).", - ConflictsWith: []string{"in.account", "in.database"}, + Type: schema.TypeString, + Optional: true, + Description: "Returns records for the current schema in use or a specified schema (schema_name).", + ExactlyOneOf: []string{"in.0.account", "in.0.database", "in.0.schema"}, }, }, }, diff --git a/pkg/datasources/dynamic_tables_acceptance_test.go b/pkg/datasources/dynamic_tables_acceptance_test.go index 830eb6a1b7..4a3ae7d9ce 100644 --- a/pkg/datasources/dynamic_tables_acceptance_test.go +++ b/pkg/datasources/dynamic_tables_acceptance_test.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "regexp" "strings" "testing" @@ -79,10 +80,71 @@ func TestAcc_DynamicTables_complete(t *testing.T) { resource.TestCheckResourceAttrSet(dataSourceName, "records.0.data_timestamp"), ), }, + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: variableSet1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "records.#", "1"), + ), + }, + }, + }) +} + +func TestAcc_DynamicTables_badCombination(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: dynamicTablesDatasourceConfigDbAndSchema(), + ExpectError: regexp.MustCompile("Invalid combination of arguments"), + }, }, }) } +func TestAcc_DynamicTables_emptyIn(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: dynamicTablesDatasourceEmptyIn(), + ExpectError: regexp.MustCompile("Invalid combination of arguments"), + }, + }, + }) +} + +func dynamicTablesDatasourceConfigDbAndSchema() string { + return fmt.Sprintf(` +data "snowflake_dynamic_tables" "dts" { + in { + database = "%s" + schema = "%s" + } +} +`, acc.TestDatabaseName, acc.TestSchemaName) +} + +func dynamicTablesDatasourceEmptyIn() string { + return ` +data "snowflake_dynamic_tables" "dts" { + in { + } +} +` +} + func testAccCheckDynamicTableDestroy(s *terraform.State) error { db := acc.TestAccProvider.Meta().(*sql.DB) client := sdk.NewClientFromDB(db) diff --git a/pkg/datasources/testdata/TestAcc_DynamicTables_complete/1/test.tf b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/1/test.tf index e338862811..a0cd96c972 100644 --- a/pkg/datasources/testdata/TestAcc_DynamicTables_complete/1/test.tf +++ b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/1/test.tf @@ -1,5 +1,3 @@ - - resource "snowflake_table" "t" { database = var.database schema = var.schema diff --git a/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/test.tf b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/test.tf new file mode 100644 index 0000000000..e42db3daf2 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/test.tf @@ -0,0 +1,36 @@ +resource "snowflake_table" "t" { + database = var.database + schema = var.schema + name = var.table_name + change_tracking = true + column { + name = "id" + type = "NUMBER(38,0)" + } +} + +resource "snowflake_dynamic_table" "dt" { + depends_on = [snowflake_table.t] + name = var.name + database = var.database + schema = var.schema + target_lag { + maximum_duration = "2 minutes" + } + warehouse = var.warehouse + query = var.query + comment = var.comment +} + +data "snowflake_dynamic_tables" "dts" { + like { + pattern = snowflake_dynamic_table.dt.name + } + in { + schema = "\"${snowflake_dynamic_table.dt.database}\".\"${snowflake_dynamic_table.dt.schema}\"" + } + starts_with = snowflake_dynamic_table.dt.name + limit { + rows = 1 + } +} diff --git a/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/variables.tf b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/variables.tf new file mode 100644 index 0000000000..81bcd62944 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_DynamicTables_complete/2/variables.tf @@ -0,0 +1,29 @@ + + +variable "name" { + type = string +} + +variable "database" { + type = string +} + +variable "schema" { + type = string +} + +variable "warehouse" { + type = string +} + +variable "query" { + type = string +} + +variable "comment" { + type = string +} + +variable "table_name" { + type = string +} diff --git a/pkg/resources/database.go b/pkg/resources/database.go index 2bc34856a8..9bf6dd8e97 100644 --- a/pkg/resources/database.go +++ b/pkg/resources/database.go @@ -190,8 +190,10 @@ func ReadDatabase(d *schema.ResourceData, meta interface{}) error { database, err := client.Databases.ShowByID(ctx, id) if err != nil { - return err + d.SetId("") + return nil } + if err := d.Set("name", database.Name); err != nil { return err } diff --git a/pkg/resources/database_acceptance_test.go b/pkg/resources/database_acceptance_test.go index c47dd58df0..21b20c506f 100644 --- a/pkg/resources/database_acceptance_test.go +++ b/pkg/resources/database_acceptance_test.go @@ -1,14 +1,21 @@ package resources_test import ( + "context" "fmt" "os" "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/stretchr/testify/require" ) func TestAcc_DatabaseWithUnderscore(t *testing.T) { @@ -83,6 +90,50 @@ func TestAcc_Database(t *testing.T) { }) } +func TestAcc_DatabaseRemovedOutsideOfTerraform(t *testing.T) { + id := generateUnsafeExecuteTestDatabaseName(t) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckDatabaseExistence(t, id, false), + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestNameDirectory(), + ConfigVariables: map[string]config.Variable{ + "db": config.StringVariable(id), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{plancheck.ExpectNonEmptyPlan()}, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_database.db", "name", id), + resource.TestCheckResourceAttr("snowflake_database.db", "comment", "test comment"), + testAccCheckDatabaseExistence(t, id, true), + ), + }, + { + PreConfig: func() { dropDatabaseOutsideTerraform(t, id) }, + ConfigDirectory: config.TestNameDirectory(), + ConfigVariables: map[string]config.Variable{ + "db": config.StringVariable(id), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{plancheck.ExpectNonEmptyPlan()}, + }, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_database.db", "name", id), + resource.TestCheckResourceAttr("snowflake_database.db", "comment", "test comment"), + testAccCheckDatabaseExistence(t, id, true), + ), + }, + }, + }) +} + func dbConfig(prefix string) string { s := ` resource "snowflake_database" "db" { @@ -103,3 +154,35 @@ resource "snowflake_database" "db" { ` return fmt.Sprintf(s, prefix) } + +func dropDatabaseOutsideTerraform(t *testing.T, id string) { + t.Helper() + + client, err := sdk.NewDefaultClient() + require.NoError(t, err) + ctx := context.Background() + + err = client.Databases.Drop(ctx, sdk.NewAccountObjectIdentifier(id), &sdk.DropDatabaseOptions{}) + require.NoError(t, err) +} + +func testAccCheckDatabaseExistence(t *testing.T, id string, shouldExist bool) func(state *terraform.State) error { + t.Helper() + return func(state *terraform.State) error { + client, err := sdk.NewDefaultClient() + require.NoError(t, err) + ctx := context.Background() + + _, err = client.Databases.ShowByID(ctx, sdk.NewAccountObjectIdentifier(id)) + if shouldExist { + if err != nil { + return fmt.Errorf("error while retrieving database %s, err = %w", id, err) + } + } else { + if err == nil { + return fmt.Errorf("database %v still exists", id) + } + } + return nil + } +} diff --git a/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/test.tf b/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/test.tf new file mode 100644 index 0000000000..f0d3470e09 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/test.tf @@ -0,0 +1,4 @@ +resource "snowflake_database" "db" { + name = var.db + comment = "test comment" +} diff --git a/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/variables.tf b/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/variables.tf new file mode 100644 index 0000000000..5ed7b249f5 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_DatabaseRemovedOutsideOfTerraform/variables.tf @@ -0,0 +1,3 @@ +variable "db" { + type = string +} diff --git a/pkg/resources/unsafe_execute_acceptance_test.go b/pkg/resources/unsafe_execute_acceptance_test.go index 3a35b04caa..2e48392184 100644 --- a/pkg/resources/unsafe_execute_acceptance_test.go +++ b/pkg/resources/unsafe_execute_acceptance_test.go @@ -667,27 +667,6 @@ func generateUnsafeExecuteTestRoleName(t *testing.T) string { return fmt.Sprintf("UNSAFE_EXECUTE_TEST_ROLE_%d", id) } -func testAccCheckDatabaseExistence(t *testing.T, id string, shouldExist bool) func(state *terraform.State) error { - t.Helper() - return func(state *terraform.State) error { - client, err := sdk.NewDefaultClient() - require.NoError(t, err) - ctx := context.Background() - - _, err = client.Databases.ShowByID(ctx, sdk.NewAccountObjectIdentifier(id)) - if shouldExist { - if err != nil { - return fmt.Errorf("error while retrieving database %s, err = %w", id, err) - } - } else { - if err == nil { - return fmt.Errorf("database %v still exists", id) - } - } - return nil - } -} - func createResourcesForExecuteUnsafeTestCaseForGrants(t *testing.T, dbId string, roleId string) { t.Helper() diff --git a/pkg/sdk/event_tables_dto_builders_gen.go b/pkg/sdk/event_tables_dto_builders_gen.go index 9d25168bbc..c7ab428c05 100644 --- a/pkg/sdk/event_tables_dto_builders_gen.go +++ b/pkg/sdk/event_tables_dto_builders_gen.go @@ -2,8 +2,6 @@ package sdk -import () - func NewCreateEventTableRequest( name SchemaObjectIdentifier, ) *CreateEventTableRequest { @@ -57,7 +55,7 @@ func (s *CreateEventTableRequest) WithComment(Comment *string) *CreateEventTable return s } -func (s *CreateEventTableRequest) WithRowAccessPolicy(RowAccessPolicy *RowAccessPolicy) *CreateEventTableRequest { +func (s *CreateEventTableRequest) WithRowAccessPolicy(RowAccessPolicy *TableRowAccessPolicy) *CreateEventTableRequest { s.RowAccessPolicy = RowAccessPolicy return s } diff --git a/pkg/sdk/event_tables_dto_gen.go b/pkg/sdk/event_tables_dto_gen.go index c043c86b21..3a4b5ca7d5 100644 --- a/pkg/sdk/event_tables_dto_gen.go +++ b/pkg/sdk/event_tables_dto_gen.go @@ -21,7 +21,7 @@ type CreateEventTableRequest struct { DefaultDdlCollation *string CopyGrants *bool Comment *string - RowAccessPolicy *RowAccessPolicy + RowAccessPolicy *TableRowAccessPolicy Tag []TagAssociation } diff --git a/pkg/sdk/event_tables_gen.go b/pkg/sdk/event_tables_gen.go index b4e641a5a9..a2f2f7ef52 100644 --- a/pkg/sdk/event_tables_gen.go +++ b/pkg/sdk/event_tables_gen.go @@ -29,7 +29,7 @@ type CreateEventTableOptions struct { DefaultDdlCollation *string `ddl:"parameter,single_quotes" sql:"DEFAULT_DDL_COLLATION"` CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` } diff --git a/pkg/sdk/event_tables_gen_test.go b/pkg/sdk/event_tables_gen_test.go index 006cb2cd98..af08c6eee2 100644 --- a/pkg/sdk/event_tables_gen_test.go +++ b/pkg/sdk/event_tables_gen_test.go @@ -37,7 +37,7 @@ func TestEventTables_Create(t *testing.T) { opts.CopyGrants = Bool(true) opts.Comment = String("test") pn := NewSchemaObjectIdentifier(random.StringN(4), random.StringN(4), random.StringN(4)) - opts.RowAccessPolicy = &RowAccessPolicy{ + opts.RowAccessPolicy = &TableRowAccessPolicy{ Name: pn, On: []string{"c1", "c2"}, }