From 1ccc8641106a3ceb4de813ce7c0e5077ead5272e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Tue, 30 Jan 2024 08:29:52 +0100 Subject: [PATCH] feat: Use API integration from SDK (#2429) - Removed old api integration implementation from resource. - Migrated tests and added more. - Added not listed in docs ALTER options (`SET AZURE_TENANT_ID` and `SET GOOGLE_AUDIENCE`) to the SDK. Added tests. --- pkg/resources/api_integration.go | 267 +++++++----- .../api_integration_acceptance_test.go | 410 +++++++++++++++--- pkg/resources/api_integration_test.go | 128 ------ .../TestAcc_ApiIntegration_aws/1/test.tf | 10 + .../TestAcc_ApiIntegration_aws/1/variables.tf | 32 ++ .../TestAcc_ApiIntegration_azure/1/test.tf | 11 + .../1/variables.tf | 31 ++ .../1/test.tf | 10 + .../1/variables.tf | 32 ++ .../2/test.tf | 11 + .../2/variables.tf | 31 ++ .../TestAcc_ApiIntegration_google/1/test.tf | 9 + .../1/variables.tf | 24 + pkg/sdk/api_integrations_def.go | 14 +- pkg/sdk/api_integrations_dto_builders_gen.go | 18 + pkg/sdk/api_integrations_dto_gen.go | 6 + pkg/sdk/api_integrations_gen.go | 6 + pkg/sdk/api_integrations_gen_test.go | 12 +- pkg/sdk/api_integrations_impl_gen.go | 6 + pkg/sdk/api_integrations_validations_gen.go | 12 +- .../api_integrations_gen_integration_test.go | 30 ++ pkg/snowflake/api_integration.go | 39 -- pkg/snowflake/api_integration_test.go | 27 -- 23 files changed, 793 insertions(+), 383 deletions(-) delete mode 100644 pkg/resources/api_integration_test.go create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_google/1/test.tf create mode 100644 pkg/resources/testdata/TestAcc_ApiIntegration_google/1/variables.tf delete mode 100644 pkg/snowflake/api_integration.go delete mode 100644 pkg/snowflake/api_integration_test.go diff --git a/pkg/resources/api_integration.go b/pkg/resources/api_integration.go index d582782268..e97b8e2bb2 100644 --- a/pkg/resources/api_integration.go +++ b/pkg/resources/api_integration.go @@ -1,12 +1,14 @@ package resources import ( + "context" "database/sql" "fmt" "log" "strings" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -22,6 +24,7 @@ var apiIntegrationSchema = map[string]*schema.Schema{ Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{"aws_api_gateway", "aws_private_api_gateway", "azure_api_management", "aws_gov_api_gateway", "aws_gov_private_api_gateway", "google_api_gateway"}, false), + ForceNew: true, Description: "Specifies the HTTPS proxy service type.", }, "api_aws_role_arn": { @@ -127,41 +130,81 @@ func APIIntegration() *schema.Resource { } } +func toApiIntegrationEndpointPrefix(paths []string) []sdk.ApiIntegrationEndpointPrefix { + allowedPrefixes := make([]sdk.ApiIntegrationEndpointPrefix, len(paths)) + for i, prefix := range paths { + allowedPrefixes[i] = sdk.ApiIntegrationEndpointPrefix{Path: prefix} + } + return allowedPrefixes +} + // CreateAPIIntegration implements schema.CreateFunc. func CreateAPIIntegration(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - name := d.Get("name").(string) + ctx := context.Background() + client := sdk.NewClientFromDB(db) - stmt := snowflake.NewAPIIntegrationBuilder(name).Create() + name := d.Get("name").(string) + id := sdk.NewAccountObjectIdentifier(name) + enabled := d.Get("enabled").(bool) - // Set required fields - stmt.SetBool(`ENABLED`, d.Get("enabled").(bool)) + allowedPrefixesRaw := expandStringList(d.Get("api_allowed_prefixes").([]interface{})) + allowedPrefixes := toApiIntegrationEndpointPrefix(allowedPrefixesRaw) - stmt.SetStringList("API_ALLOWED_PREFIXES", expandStringList(d.Get("api_allowed_prefixes").([]interface{}))) + createRequest := sdk.NewCreateApiIntegrationRequest(id, allowedPrefixes, enabled) - // Set optional fields if _, ok := d.GetOk("api_blocked_prefixes"); ok { - stmt.SetStringList("API_BLOCKED_PREFIXES", expandStringList(d.Get("api_blocked_prefixes").([]interface{}))) + blockedPrefixesRaw := expandStringList(d.Get("api_blocked_prefixes").([]interface{})) + createRequest.WithApiBlockedPrefixes(toApiIntegrationEndpointPrefix(blockedPrefixesRaw)) } - if _, ok := d.GetOk("api_key"); ok { - stmt.SetString("API_KEY", d.Get("api_key").(string)) + if v, ok := d.GetOk("comment"); ok { + createRequest.WithComment(sdk.String(v.(string))) } - if _, ok := d.GetOk("comment"); ok { - stmt.SetString("COMMENT", d.Get("comment").(string)) - } - - // Now, set the API provider - if err := setAPIProviderSettings(d, stmt); err != nil { - return err + apiProvider := d.Get("api_provider").(string) + switch apiProvider { + case "aws_api_gateway", "aws_private_api_gateway", "aws_gov_api_gateway", "aws_gov_private_api_gateway": + roleArn, ok := d.GetOk("api_aws_role_arn") + if !ok { + return fmt.Errorf("if you use AWS api provider you must specify an api_aws_role_arn") + } + awsParams := sdk.NewAwsApiParamsRequest(sdk.ApiIntegrationAwsApiProviderType(apiProvider), roleArn.(string)) + if v, ok := d.GetOk("api_key"); ok { + awsParams.WithApiKey(sdk.String(v.(string))) + } + createRequest.WithAwsApiProviderParams(awsParams) + case "azure_api_management": + tenantId, ok := d.GetOk("azure_tenant_id") + if !ok { + return fmt.Errorf("if you use the Azure api provider you must specify an azure_tenant_id") + } + applicationId, ok := d.GetOk("azure_ad_application_id") + if !ok { + return fmt.Errorf("if you use the Azure api provider you must specify an azure_ad_application_id") + } + azureParams := sdk.NewAzureApiParamsRequest(tenantId.(string), applicationId.(string)) + if v, ok := d.GetOk("api_key"); ok { + azureParams.WithApiKey(sdk.String(v.(string))) + } + createRequest.WithAzureApiProviderParams(azureParams) + case "google_api_gateway": + audience, ok := d.GetOk("google_audience") + if !ok { + return fmt.Errorf("if you use GCP api provider you must specify a google_audience") + } + googleParams := sdk.NewGoogleApiParamsRequest(audience.(string)) + createRequest.WithGoogleApiProviderParams(googleParams) + default: + return fmt.Errorf("unexpected provider %v", apiProvider) } - if err := snowflake.Exec(db, stmt.Statement()); err != nil { + err := client.ApiIntegrations.Create(ctx, createRequest) + if err != nil { return fmt.Errorf("error creating api integration: %w", err) } - d.SetId(name) + d.SetId(helpers.EncodeSnowflakeID(id)) return ReadAPIIntegration(d, meta) } @@ -169,101 +212,104 @@ func CreateAPIIntegration(d *schema.ResourceData, meta interface{}) error { // ReadAPIIntegration implements schema.ReadFunc. func ReadAPIIntegration(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - id := d.Id() - - stmt := snowflake.NewAPIIntegrationBuilder(id).Show() - row := snowflake.QueryRow(db, stmt) - - // Some properties can come from the SHOW INTEGRATION call + ctx := context.Background() + client := sdk.NewClientFromDB(db) + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - s, err := snowflake.ScanAPIIntegration(row) + integration, err := client.ApiIntegrations.ShowByID(ctx, id) if err != nil { - // If no such resource exists, it is not an error but rather not exist - if err.Error() == snowflake.ErrNoRowInRS { - d.SetId("") - return nil - } - return fmt.Errorf("could not show api integration: %w", err) + log.Printf("[DEBUG] api integration (%s) not found", d.Id()) + d.SetId("") + return err } // Note: category must be API or something is broken - if c := s.Category.String; c != "API" { + if c := integration.Category; c != "API" { return fmt.Errorf("expected %v to be an api integration, got %v", id, c) } - if err := d.Set("name", s.Name.String); err != nil { + if err := d.Set("name", integration.Name); err != nil { return err } - if err := d.Set("comment", s.Comment.String); err != nil { + if err := d.Set("comment", integration.Comment); err != nil { return err } - if err := d.Set("created_on", s.CreatedOn.String); err != nil { + if err := d.Set("created_on", integration.CreatedOn.String()); err != nil { return err } - if err := d.Set("enabled", s.Enabled.Bool); err != nil { + if err := d.Set("enabled", integration.Enabled); err != nil { return err } // Some properties come from the DESCRIBE INTEGRATION call // We need to grab them in a loop - var k, pType string - var v, unused interface{} - stmt = snowflake.NewAPIIntegrationBuilder(id).Describe() - rows, err := db.Query(stmt) + + integrationProperties, err := client.ApiIntegrations.Describe(ctx, id) if err != nil { return fmt.Errorf("could not describe api integration: %w", err) } - defer rows.Close() - for rows.Next() { - if err := rows.Scan(&k, &pType, &v, &unused); err != nil { - return err - } - switch k { + + for _, property := range integrationProperties { + name := property.Name + value := property.Value + switch name { case "ENABLED": // We set this using the SHOW INTEGRATION call so let's ignore it here case "API_ALLOWED_PREFIXES": - if err := d.Set("api_allowed_prefixes", strings.Split(v.(string), ",")); err != nil { + if err := d.Set("api_allowed_prefixes", strings.Split(value, ",")); err != nil { return err } case "API_BLOCKED_PREFIXES": - if val := v.(string); val != "" { + if val := value; val != "" { if err := d.Set("api_blocked_prefixes", strings.Split(val, ",")); err != nil { return err } } case "API_AWS_IAM_USER_ARN": - if err := d.Set("api_aws_iam_user_arn", v.(string)); err != nil { + if err := d.Set("api_aws_iam_user_arn", value); err != nil { return err } case "API_AWS_ROLE_ARN": - if err := d.Set("api_aws_role_arn", v.(string)); err != nil { + if err := d.Set("api_aws_role_arn", value); err != nil { return err } case "API_AWS_EXTERNAL_ID": - if err := d.Set("api_aws_external_id", v.(string)); err != nil { + if err := d.Set("api_aws_external_id", value); err != nil { return err } case "AZURE_CONSENT_URL": - if err := d.Set("azure_consent_url", v.(string)); err != nil { + if err := d.Set("azure_consent_url", value); err != nil { return err } case "AZURE_MULTI_TENANT_APP_NAME": - if err := d.Set("azure_multi_tenant_app_name", v.(string)); err != nil { + if err := d.Set("azure_multi_tenant_app_name", value); err != nil { + return err + } + case "AZURE_TENANT_ID": + if err := d.Set("azure_tenant_id", value); err != nil { + return err + } + case "AZURE_AD_APPLICATION_ID": + if err := d.Set("azure_ad_application_id", value); err != nil { return err } case "GOOGLE_AUDIENCE": - if err := d.Set("google_audience", v.(string)); err != nil { + if err := d.Set("google_audience", value); err != nil { return err } case "API_GCP_SERVICE_ACCOUNT": - if err := d.Set("api_gcp_service_account", v.(string)); err != nil { + if err := d.Set("api_gcp_service_account", value); err != nil { + return err + } + case "API_PROVIDER": + if err := d.Set("api_provider", strings.ToLower(value)); err != nil { return err } default: - log.Printf("[WARN] unexpected api integration property %v returned from Snowflake", k) + log.Printf("[WARN] unexpected api integration property %v returned from Snowflake", name) } } @@ -273,72 +319,83 @@ func ReadAPIIntegration(d *schema.ResourceData, meta interface{}) error { // UpdateAPIIntegration implements schema.UpdateFunc. func UpdateAPIIntegration(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - id := d.Id() - - stmt := snowflake.NewAPIIntegrationBuilder(id).Alter() + ctx := context.Background() + client := sdk.NewClientFromDB(db) + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) var runSetStatement bool - + setRequest := sdk.NewApiIntegrationSetRequest() if d.HasChange("enabled") { runSetStatement = true - stmt.SetBool(`ENABLED`, d.Get("enabled").(bool)) + setRequest.WithEnabled(sdk.Bool(d.Get("enabled").(bool))) } if d.HasChange("api_allowed_prefixes") { runSetStatement = true - stmt.SetStringList("API_ALLOWED_PREFIXES", expandStringList(d.Get("api_allowed_prefixes").([]interface{}))) - } - - if d.HasChange("api_key") { - runSetStatement = true - stmt.SetString("API_KEY", d.Get("api_key").(string)) + setRequest.WithApiAllowedPrefixes(toApiIntegrationEndpointPrefix(expandStringList(d.Get("api_allowed_prefixes").([]interface{})))) } if d.HasChange("comment") { runSetStatement = true - stmt.SetString("COMMENT", d.Get("comment").(string)) + setRequest.WithComment(sdk.String(d.Get("comment").(string))) } // We need to UNSET this if we remove all api blocked prefixes. if d.HasChange("api_blocked_prefixes") { v := d.Get("api_blocked_prefixes").([]interface{}) if len(v) == 0 { - if err := snowflake.Exec(db, fmt.Sprintf(`ALTER API INTEGRATION %v UNSET API_BLOCKED_PREFIXES`, id)); err != nil { + err := client.ApiIntegrations.Alter(ctx, sdk.NewAlterApiIntegrationRequest(id).WithUnset(sdk.NewApiIntegrationUnsetRequest().WithApiBlockedPrefixes(sdk.Bool(true)))) + if err != nil { return fmt.Errorf("error unsetting api_blocked_prefixes: %w", err) } } else { runSetStatement = true - stmt.SetStringList("API_BLOCKED_PREFIXES", expandStringList(v)) + setRequest.WithApiBlockedPrefixes(toApiIntegrationEndpointPrefix(expandStringList(v))) } } - if d.HasChange("api_provider") { - runSetStatement = true - err := setAPIProviderSettings(d, stmt) - if err != nil { - return err - } - } else { + apiProvider := d.Get("api_provider").(string) + switch apiProvider { + case "aws_api_gateway", "aws_private_api_gateway", "aws_gov_api_gateway", "aws_gov_private_api_gateway": + awsParams := sdk.NewSetAwsApiParamsRequest() if d.HasChange("api_aws_role_arn") { + awsParams.WithApiAwsRoleArn(sdk.String(d.Get("api_aws_role_arn").(string))) + } + if d.HasChange("api_key") { + awsParams.WithApiKey(sdk.String(d.Get("api_key").(string))) + } + if *awsParams != *sdk.NewSetAwsApiParamsRequest() { runSetStatement = true - stmt.SetString("API_AWS_ROLE_ARN", d.Get("api_aws_role_arn").(string)) + setRequest.WithAwsParams(awsParams) } + case "azure_api_management": + azureParams := sdk.NewSetAzureApiParamsRequest() if d.HasChange("azure_tenant_id") { - runSetStatement = true - stmt.SetString("AZURE_TENANT_ID", d.Get("azure_tenant_id").(string)) + azureParams.WithAzureTenantId(sdk.String(d.Get("azure_tenant_id").(string))) } if d.HasChange("azure_ad_application_id") { + azureParams.WithAzureAdApplicationId(sdk.String(d.Get("azure_ad_application_id").(string))) + } + if d.HasChange("api_key") { + azureParams.WithApiKey(sdk.String(d.Get("api_key").(string))) + } + if *azureParams != *sdk.NewSetAzureApiParamsRequest() { runSetStatement = true - stmt.SetString("AZURE_AD_APPLICATION_ID", d.Get("azure_ad_application_id").(string)) + setRequest.WithAzureParams(azureParams) } + case "google_api_gateway": if d.HasChange("google_audience") { runSetStatement = true - stmt.SetString("GOOGLE_AUDIENCE", d.Get("google_audience").(string)) + googleParams := sdk.NewSetGoogleApiParamsRequest(d.Get("google_audience").(string)) + setRequest.WithGoogleParams(googleParams) } + default: + return fmt.Errorf("unexpected provider %v", apiProvider) } if runSetStatement { - if err := snowflake.Exec(db, stmt.Statement()); err != nil { + err := client.ApiIntegrations.Alter(ctx, sdk.NewAlterApiIntegrationRequest(id).WithSet(setRequest)) + if err != nil { return fmt.Errorf("error updating api integration: %w", err) } } @@ -348,41 +405,17 @@ func UpdateAPIIntegration(d *schema.ResourceData, meta interface{}) error { // DeleteAPIIntegration implements schema.DeleteFunc. func DeleteAPIIntegration(d *schema.ResourceData, meta interface{}) error { - return DeleteResource("", snowflake.NewAPIIntegrationBuilder)(d, meta) -} - -func setAPIProviderSettings(data *schema.ResourceData, stmt snowflake.SettingBuilder) error { - apiProvider := data.Get("api_provider").(string) - stmt.SetRaw("API_PROVIDER=" + apiProvider) - - switch apiProvider { - case "aws_api_gateway", "aws_private_api_gateway", "aws_gov_api_gateway", "aws_gov_private_api_gateway": - v, ok := data.GetOk("api_aws_role_arn") - if !ok { - return fmt.Errorf("if you use AWS api provider you must specify an api_aws_role_arn") - } - stmt.SetString(`API_AWS_ROLE_ARN`, v.(string)) - case "azure_api_management": - v, ok := data.GetOk("azure_tenant_id") - if !ok { - return fmt.Errorf("if you use the Azure api provider you must specify an azure_tenant_id") - } - stmt.SetString(`AZURE_TENANT_ID`, v.(string)) + db := meta.(*sql.DB) + ctx := context.Background() + client := sdk.NewClientFromDB(db) + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - v, ok = data.GetOk("azure_ad_application_id") - if !ok { - return fmt.Errorf("if you use the Azure api provider you must specify an azure_ad_application_id") - } - stmt.SetString(`AZURE_AD_APPLICATION_ID`, v.(string)) - case "google_api_gateway": - v, ok := data.GetOk("google_audience") - if !ok { - return fmt.Errorf("if you use GCP api provider you must specify a google_audience") - } - stmt.SetString(`GOOGLE_AUDIENCE`, v.(string)) - default: - return fmt.Errorf("unexpected provider %v", apiProvider) + err := client.ApiIntegrations.Drop(ctx, sdk.NewDropApiIntegrationRequest(id)) + if err != nil { + return err } + d.SetId("") + return nil } diff --git a/pkg/resources/api_integration_acceptance_test.go b/pkg/resources/api_integration_acceptance_test.go index b07d0c97e5..da54da7965 100644 --- a/pkg/resources/api_integration_acceptance_test.go +++ b/pkg/resources/api_integration_acceptance_test.go @@ -1,107 +1,403 @@ package resources_test import ( + "context" + "database/sql" "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/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func TestAcc_ApiIntegration(t *testing.T) { - if _, ok := os.LookupEnv("SKIP_API_INTEGRATION_TESTS"); ok { - t.Skip("Skipping TestAcc_ApiIntegration") - } +func TestAcc_ApiIntegration_aws(t *testing.T) { + const dummyAwsPrefix = "https://123456.execute-api.us-west-2.amazonaws.com/dev/" + const dummyAwsOtherPrefix = "https://123456.execute-api.us-west-2.amazonaws.com/prod/" + const dummyAwsApiRoleArn = "arn:aws:iam::000000000001:/role/test" + const dummyAwsOtherApiRoleArn = "arn:aws:iam::000000000001:/role/other" - apiIntNameAWS := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - apiIntNameAzure := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - apiIntNameGCP := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + comment := "acceptance test" + key := "12345" + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(name), + "api_provider": config.StringVariable("aws_api_gateway"), + "api_aws_role_arn": config.StringVariable(dummyAwsApiRoleArn), + "api_allowed_prefixes": config.ListVariable( + config.StringVariable(dummyAwsPrefix), + ), + "api_blocked_prefixes": config.ListVariable( + config.StringVariable(dummyAwsOtherPrefix), + ), + "api_key": config.StringVariable(key), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + } + } + m2 := m() + m2["api_aws_role_arn"] = config.StringVariable(dummyAwsOtherApiRoleArn) + m2["api_key"] = config.StringVariable("other_key") + m2["api_blocked_prefixes"] = config.ListVariable() + m2["api_allowed_prefixes"] = config.ListVariable( + config.StringVariable(dummyAwsOtherPrefix), + ) + m2["comment"] = config.StringVariable("different comment") resource.Test(t, resource.TestCase{ - Providers: acc.TestAccProviders(), - PreCheck: func() { acc.TestAccPreCheck(t) }, - CheckDestroy: nil, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckApiIntegrationDestroy, Steps: []resource.TestStep{ { - Config: apiIntegrationConfigAWS(apiIntNameAWS, []string{"https://123456.execute-api.us-west-2.amazonaws.com/prod/"}), + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: m(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "name", name), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_provider", "aws_api_gateway"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_aws_role_arn", dummyAwsApiRoleArn), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_allowed_prefixes.0", dummyAwsPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_blocked_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_blocked_prefixes.0", dummyAwsOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "comment", comment), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "created_on"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_iam_user_arn"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_external_id"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_key", key), + ), + }, + // change parameters + { + ConfigDirectory: acc.ConfigurationSameAsStepN(1), + ConfigVariables: m2, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "name", apiIntNameAWS), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "name", name), resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_provider", "aws_api_gateway"), - resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "comment", "acceptance test"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_aws_role_arn", dummyAwsOtherApiRoleArn), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_allowed_prefixes.0", dummyAwsOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_blocked_prefixes.#", "0"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "comment", "different comment"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "created_on"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_iam_user_arn"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_aws_external_id"), - resource.TestCheckResourceAttrSet("snowflake_api_integration.test_aws_int", "api_key"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_aws_int", "api_key", "other_key"), ), }, + // IMPORT + { + ConfigVariables: m2, + ResourceName: "snowflake_api_integration.test_aws_int", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"api_key"}, + }, + }, + }) +} + +func TestAcc_ApiIntegration_azure(t *testing.T) { + const dummyAzurePrefix = "https://apim-hello-world.azure-api.net/dev" + const dummyAzureOtherPrefix = "https://apim-hello-world.azure-api.net/prod" + const dummyAzureTenantId = "00000000-0000-0000-0000-000000000000" + const dummyAzureOtherTenantId = "11111111-1111-1111-1111-111111111111" + const dummyAzureAdApplicationId = "22222222-2222-2222-2222-222222222222" + const dummyAzureOtherAdApplicationId = "33333333-3333-3333-3333-333333333333" + + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + comment := "acceptance test" + key := "12345" + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(name), + "azure_tenant_id": config.StringVariable(dummyAzureTenantId), + "azure_ad_application_id": config.StringVariable(dummyAzureAdApplicationId), + "api_allowed_prefixes": config.ListVariable( + config.StringVariable(dummyAzurePrefix), + ), + "api_blocked_prefixes": config.ListVariable( + config.StringVariable(dummyAzureOtherPrefix), + ), + "api_key": config.StringVariable(key), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + } + } + m2 := m() + m2["azure_ad_application_id"] = config.StringVariable(dummyAzureOtherAdApplicationId) + m2["azure_tenant_id"] = config.StringVariable(dummyAzureOtherTenantId) + m2["api_key"] = config.StringVariable("other_key") + m2["api_blocked_prefixes"] = config.ListVariable() + m2["api_allowed_prefixes"] = config.ListVariable( + config.StringVariable(dummyAzureOtherPrefix), + ) + m2["comment"] = config.StringVariable("different comment") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckApiIntegrationDestroy, + Steps: []resource.TestStep{ { - Config: apiIntegrationConfigAzure(apiIntNameAzure, []string{"https://apim-hello-world.azure-api.net/"}), + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: m(), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "name", apiIntNameAzure), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "name", name), resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_provider", "azure_api_management"), - resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "comment", "acceptance test"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "azure_tenant_id", dummyAzureTenantId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "azure_ad_application_id", dummyAzureAdApplicationId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_allowed_prefixes.0", dummyAzurePrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_blocked_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_blocked_prefixes.0", dummyAzureOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "comment", comment), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "created_on"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_multi_tenant_app_name"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_consent_url"), - resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "api_key"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_key", key), ), }, + // change parameters { - Config: apiIntegrationConfigGCP(apiIntNameGCP, []string{"https://gateway-id-123456.uc.gateway.dev/"}), + ConfigDirectory: acc.ConfigurationSameAsStepN(1), + ConfigVariables: m2, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "name", apiIntNameGCP), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "name", name), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_provider", "azure_api_management"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "azure_tenant_id", dummyAzureOtherTenantId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "azure_ad_application_id", dummyAzureOtherAdApplicationId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_allowed_prefixes.0", dummyAzureOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_blocked_prefixes.#", "0"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "comment", "different comment"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "created_on"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_multi_tenant_app_name"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_azure_int", "azure_consent_url"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_azure_int", "api_key", "other_key"), + ), + }, + // IMPORT + { + ConfigVariables: m2, + ResourceName: "snowflake_api_integration.test_azure_int", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"api_key"}, + }, + }, + }) +} + +func TestAcc_ApiIntegration_google(t *testing.T) { + const dummyGooglePrefix = "https://gateway-id-123456.uc.gateway.dev/prod" + const dummyGoogleOtherPrefix = "https://gateway-id-123456.uc.gateway.dev/dev" + const dummyGoogleAudience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog" + const dummyGoogleOtherAudience = "api-gateway-id-666777.apigateway.gcp-project.cloud.goog" + + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + comment := "acceptance test" + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(name), + "google_audience": config.StringVariable(dummyGoogleAudience), + "api_allowed_prefixes": config.ListVariable( + config.StringVariable(dummyGooglePrefix), + ), + "api_blocked_prefixes": config.ListVariable( + config.StringVariable(dummyGoogleOtherPrefix), + ), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + } + } + m2 := m() + m2["google_audience"] = config.StringVariable(dummyGoogleOtherAudience) + m2["api_blocked_prefixes"] = config.ListVariable() + m2["api_allowed_prefixes"] = config.ListVariable( + config.StringVariable(dummyGoogleOtherPrefix), + ) + m2["comment"] = config.StringVariable("different comment") + m2["api_aws_role_arn"] = config.StringVariable(dummyGoogleOtherAudience) + m2["api_key"] = config.StringVariable("other_key") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckApiIntegrationDestroy, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: m(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "name", name), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_provider", "google_api_gateway"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "google_audience", dummyGoogleAudience), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_allowed_prefixes.0", dummyGooglePrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_blocked_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_blocked_prefixes.0", dummyGoogleOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "comment", comment), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "created_on"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "google_audience"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "api_gcp_service_account"), + resource.TestCheckNoResourceAttr("snowflake_api_integration.test_gcp_int", "api_key"), + ), + }, + // change parameters + { + ConfigDirectory: acc.ConfigurationSameAsStepN(1), + ConfigVariables: m2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "name", name), resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_provider", "google_api_gateway"), - resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "comment", "acceptance test"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "google_audience", dummyGoogleOtherAudience), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_allowed_prefixes.0", dummyGoogleOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "api_blocked_prefixes.#", "0"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_gcp_int", "comment", "different comment"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "created_on"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "google_audience"), resource.TestCheckResourceAttrSet("snowflake_api_integration.test_gcp_int", "api_gcp_service_account"), + resource.TestCheckNoResourceAttr("snowflake_api_integration.test_gcp_int", "api_key"), ), }, + // IMPORT + { + ConfigVariables: m2, + ResourceName: "snowflake_api_integration.test_gcp_int", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"api_key"}, + }, }, }) } -func apiIntegrationConfigAWS(name string, prefixes []string) string { - return fmt.Sprintf(` - resource "snowflake_api_integration" "test_aws_int" { - name = "%s" - api_provider = "aws_api_gateway" - api_aws_role_arn = "arn:aws:iam::000000000001:/role/test" - api_allowed_prefixes = %q - api_key = "12345" - comment = "acceptance test" - enabled = true - } - `, name, prefixes) -} +func TestAcc_ApiIntegration_changeApiProvider(t *testing.T) { + const dummyAwsPrefix = "https://123456.execute-api.us-west-2.amazonaws.com/dev/" + const dummyAwsOtherPrefix = "https://123456.execute-api.us-west-2.amazonaws.com/prod/" + const dummyAwsApiRoleArn = "arn:aws:iam::000000000001:/role/test" + const dummyAzurePrefix = "https://apim-hello-world.azure-api.net/dev" + const dummyAzureOtherPrefix = "https://apim-hello-world.azure-api.net/prod" + const dummyAzureTenantId = "00000000-0000-0000-0000-000000000000" + const dummyAzureAdApplicationId = "22222222-2222-2222-2222-222222222222" -func apiIntegrationConfigAzure(name string, prefixes []string) string { - return fmt.Sprintf(` - resource "snowflake_api_integration" "test_azure_int" { - name = "%s" - api_provider = "azure_api_management" - azure_tenant_id = "123456" - azure_ad_application_id = "7890" - api_allowed_prefixes = %q - api_key = "12345" - comment = "acceptance test" - enabled = true + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + comment := "acceptance test" + key := "12345" + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(name), + "api_provider": config.StringVariable("aws_api_gateway"), + "api_aws_role_arn": config.StringVariable(dummyAwsApiRoleArn), + "api_allowed_prefixes": config.ListVariable( + config.StringVariable(dummyAwsPrefix), + ), + "api_blocked_prefixes": config.ListVariable( + config.StringVariable(dummyAwsOtherPrefix), + ), + "api_key": config.StringVariable(key), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + } + } + m2 := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(name), + "azure_tenant_id": config.StringVariable(dummyAzureTenantId), + "azure_ad_application_id": config.StringVariable(dummyAzureAdApplicationId), + "api_allowed_prefixes": config.ListVariable( + config.StringVariable(dummyAzurePrefix), + ), + "api_blocked_prefixes": config.ListVariable( + config.StringVariable(dummyAzureOtherPrefix), + ), + "api_key": config.StringVariable(key), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + } } - `, name, prefixes) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckApiIntegrationDestroy, + Steps: []resource.TestStep{ + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: m(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "name", name), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_provider", "aws_api_gateway"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_aws_role_arn", dummyAwsApiRoleArn), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_allowed_prefixes.0", dummyAwsPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_blocked_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_blocked_prefixes.0", dummyAwsOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "comment", comment), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "created_on"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "api_aws_iam_user_arn"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "api_aws_external_id"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_key", key), + ), + }, + // change parameters + { + ConfigDirectory: config.TestStepDirectory(), + ConfigVariables: m2(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "name", name), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_provider", "azure_api_management"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "azure_tenant_id", dummyAzureTenantId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "azure_ad_application_id", dummyAzureAdApplicationId), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_allowed_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_allowed_prefixes.0", dummyAzurePrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_blocked_prefixes.#", "1"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_blocked_prefixes.0", dummyAzureOtherPrefix), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "comment", comment), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "created_on"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "azure_multi_tenant_app_name"), + resource.TestCheckResourceAttrSet("snowflake_api_integration.test_change", "azure_consent_url"), + resource.TestCheckResourceAttr("snowflake_api_integration.test_change", "api_key", key), + ), + }, + }, + }) } -func apiIntegrationConfigGCP(name string, prefixes []string) string { - return fmt.Sprintf(` - resource "snowflake_api_integration" "test_gcp_int" { - name = "%s" - api_provider = "google_api_gateway" - google_audience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog" - api_allowed_prefixes = %q - comment = "acceptance test" - enabled = true +func testAccCheckApiIntegrationDestroy(s *terraform.State) error { + db := acc.TestAccProvider.Meta().(*sql.DB) + client := sdk.NewClientFromDB(db) + for _, rs := range s.RootModule().Resources { + if rs.Type != "snowflake_api_integration" { + continue + } + ctx := context.Background() + id := sdk.NewAccountObjectIdentifier(rs.Primary.Attributes["name"]) + existingApiIntegration, err := client.ApiIntegrations.ShowByID(ctx, id) + if err == nil { + return fmt.Errorf("api integration %v still exists", existingApiIntegration.ID().FullyQualifiedName()) + } } - `, name, prefixes) + return nil } diff --git a/pkg/resources/api_integration_test.go b/pkg/resources/api_integration_test.go deleted file mode 100644 index b22c15c032..0000000000 --- a/pkg/resources/api_integration_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package resources_test - -import ( - "database/sql" - "testing" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" - . "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/testhelpers" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/stretchr/testify/require" -) - -func TestAPIIntegration(t *testing.T) { - r := require.New(t) - err := resources.APIIntegration().InternalValidate(provider.Provider().Schema, true) - r.NoError(err) -} - -func TestAPIIntegrationCreate(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "name": "test_api_integration", - "api_allowed_prefixes": []interface{}{"https://123456.execute-api.us-west-2.amazonaws.com/prod/"}, - "api_provider": "aws_api_gateway", - "api_aws_role_arn": "arn:aws:iam::000000000001:/role/test", - "api_key": "12345", - } - - in2 := map[string]interface{}{ - "name": "test_gov_api_integration", - "api_allowed_prefixes": []interface{}{"https://123456.execute-api.us-gov-west-1.amazonaws.com/prod/"}, - "api_provider": "aws_gov_api_gateway", - "api_aws_role_arn": "arn:aws:iam::000000000001:/role/test", - "api_key": "12345", - } - - d := schema.TestResourceDataRaw(t, resources.APIIntegration().Schema, in) - d2 := schema.TestResourceDataRaw(t, resources.APIIntegration().Schema, in2) - - r.NotNil(d) - r.NotNil(d2) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec( - `^CREATE API INTEGRATION "test_api_integration" API_PROVIDER=aws_api_gateway API_AWS_ROLE_ARN='arn:aws:iam::000000000001:/role/test' API_KEY='12345' API_ALLOWED_PREFIXES=\('https://123456.execute-api.us-west-2.amazonaws.com/prod/'\) ENABLED=true$`, - ).WillReturnResult(sqlmock.NewResult(1, 1)) - expectReadAPIIntegration(mock) - - err := resources.CreateAPIIntegration(d, db) - r.NoError(err) - }) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec( - `^CREATE API INTEGRATION "test_gov_api_integration" API_PROVIDER=aws_gov_api_gateway API_AWS_ROLE_ARN='arn:aws:iam::000000000001:/role/test' API_KEY='12345' API_ALLOWED_PREFIXES=\('https://123456.execute-api.us-gov-west-1.amazonaws.com/prod/'\) ENABLED=true$`, - ).WillReturnResult(sqlmock.NewResult(1, 1)) - expectReadGovAPIIntegration(mock) - - err := resources.CreateAPIIntegration(d2, db) - r.NoError(err) - }) -} - -func TestAPIIntegrationRead(t *testing.T) { - r := require.New(t) - - d := apiIntegration(t, "test_api_integration", map[string]interface{}{"name": "test_api_integration"}) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - expectReadAPIIntegration(mock) - - err := resources.ReadAPIIntegration(d, db) - r.NoError(err) - }) -} - -func TestAPIIntegrationDelete(t *testing.T) { - r := require.New(t) - - d := apiIntegration(t, "drop_it", map[string]interface{}{"name": "drop_it"}) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec(`DROP API INTEGRATION "drop_it"`).WillReturnResult(sqlmock.NewResult(1, 1)) - err := resources.DeleteAPIIntegration(d, db) - r.NoError(err) - }) -} - -func expectReadAPIIntegration(mock sqlmock.Sqlmock) { - showRows := sqlmock.NewRows([]string{ - "name", "type", "category", "enabled", "created_on", - }, - ).AddRow("test_api_integration", "EXTERNAL_API", "API", true, "now") - mock.ExpectQuery(`^SHOW API INTEGRATIONS LIKE 'test_api_integration'$`).WillReturnRows(showRows) - - descRows := sqlmock.NewRows([]string{ - "property", "property_type", "property_value", "property_default", - }).AddRow("ENABLED", "Boolean", true, false). - AddRow("API_KEY", "String", "12345", nil). - AddRow("API_ALLOWED_PREFIXES", "List", "https://123456.execute-api.us-west-2.amazonaws.com/prod/,https://123456.execute-api.us-west-2.amazonaws.com/staging/", nil). - AddRow("API_AWS_IAM_USER_ARN", "String", "arn:aws:iam::000000000000:/user/test", nil). - AddRow("API_AWS_ROLE_ARN", "String", "arn:aws:iam::000000000001:/role/test", nil). - AddRow("API_AWS_EXTERNAL_ID", "String", "AGreatExternalID", nil) - - mock.ExpectQuery(`DESCRIBE API INTEGRATION "test_api_integration"$`).WillReturnRows(descRows) -} - -func expectReadGovAPIIntegration(mock sqlmock.Sqlmock) { - showRows := sqlmock.NewRows([]string{ - "name", "type", "category", "enabled", "created_on", - }, - ).AddRow("test_gov_api_integration", "EXTERNAL_API", "API", true, "now") - mock.ExpectQuery(`^SHOW API INTEGRATIONS LIKE 'test_gov_api_integration'$`).WillReturnRows(showRows) - - descRows := sqlmock.NewRows([]string{ - "property", "property_type", "property_value", "property_default", - }).AddRow("ENABLED", "Boolean", true, false). - AddRow("API_KEY", "String", "12345", nil). - AddRow("API_ALLOWED_PREFIXES", "List", "https://123456.execute-api.us-gov-west-1.amazonaws.com/prod/,https://123456.execute-api.us-gov-west-1.amazonaws.com/staging/", nil). - AddRow("API_AWS_IAM_USER_ARN", "String", "arn:aws:iam::000000000000:/user/test", nil). - AddRow("API_AWS_ROLE_ARN", "String", "arn:aws:iam::000000000001:/role/test", nil). - AddRow("API_AWS_EXTERNAL_ID", "String", "AGreatExternalID", nil) - - mock.ExpectQuery(`DESCRIBE API INTEGRATION "test_gov_api_integration"$`).WillReturnRows(descRows) -} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/test.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/test.tf new file mode 100644 index 0000000000..f75e021244 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/test.tf @@ -0,0 +1,10 @@ +resource "snowflake_api_integration" "test_aws_int" { + name = var.name + api_provider = var.api_provider + api_aws_role_arn = var.api_aws_role_arn + api_allowed_prefixes = var.api_allowed_prefixes + api_blocked_prefixes = var.api_blocked_prefixes + api_key = var.api_key + comment = var.comment + enabled = var.enabled +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/variables.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/variables.tf new file mode 100644 index 0000000000..7a9d628678 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_aws/1/variables.tf @@ -0,0 +1,32 @@ +variable "name" { + type = string +} + +variable "api_provider" { + type = string +} + +variable "api_aws_role_arn" { + type = string +} + +variable "api_allowed_prefixes" { + type = list(string) +} + +variable "api_blocked_prefixes" { + type = list(string) +} + +variable "api_key" { + type = string +} + +variable "comment" { + type = string +} + +variable "enabled" { + type = bool +} + diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/test.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/test.tf new file mode 100644 index 0000000000..29ed62a9be --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_api_integration" "test_azure_int" { + name = var.name + api_provider = "azure_api_management" + azure_tenant_id = var.azure_tenant_id + azure_ad_application_id = var.azure_ad_application_id + api_allowed_prefixes = var.api_allowed_prefixes + api_blocked_prefixes = var.api_blocked_prefixes + api_key = var.api_key + comment = var.comment + enabled = var.enabled +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/variables.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/variables.tf new file mode 100644 index 0000000000..e7aeb1a64b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_azure/1/variables.tf @@ -0,0 +1,31 @@ +variable "name" { + type = string +} + +variable "azure_tenant_id" { + type = string +} + +variable "azure_ad_application_id" { + type = string +} + +variable "api_allowed_prefixes" { + type = list(string) +} + +variable "api_blocked_prefixes" { + type = list(string) +} + +variable "api_key" { + type = string +} + +variable "comment" { + type = string +} + +variable "enabled" { + type = bool +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/test.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/test.tf new file mode 100644 index 0000000000..4bcf2dc517 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/test.tf @@ -0,0 +1,10 @@ +resource "snowflake_api_integration" "test_change" { + name = var.name + api_provider = var.api_provider + api_aws_role_arn = var.api_aws_role_arn + api_allowed_prefixes = var.api_allowed_prefixes + api_blocked_prefixes = var.api_blocked_prefixes + api_key = var.api_key + comment = var.comment + enabled = var.enabled +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/variables.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/variables.tf new file mode 100644 index 0000000000..7a9d628678 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/1/variables.tf @@ -0,0 +1,32 @@ +variable "name" { + type = string +} + +variable "api_provider" { + type = string +} + +variable "api_aws_role_arn" { + type = string +} + +variable "api_allowed_prefixes" { + type = list(string) +} + +variable "api_blocked_prefixes" { + type = list(string) +} + +variable "api_key" { + type = string +} + +variable "comment" { + type = string +} + +variable "enabled" { + type = bool +} + diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/test.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/test.tf new file mode 100644 index 0000000000..6caba34efc --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_api_integration" "test_change" { + name = var.name + api_provider = "azure_api_management" + azure_tenant_id = var.azure_tenant_id + azure_ad_application_id = var.azure_ad_application_id + api_allowed_prefixes = var.api_allowed_prefixes + api_blocked_prefixes = var.api_blocked_prefixes + api_key = var.api_key + comment = var.comment + enabled = var.enabled +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/variables.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/variables.tf new file mode 100644 index 0000000000..e7aeb1a64b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_changeApiProvider/2/variables.tf @@ -0,0 +1,31 @@ +variable "name" { + type = string +} + +variable "azure_tenant_id" { + type = string +} + +variable "azure_ad_application_id" { + type = string +} + +variable "api_allowed_prefixes" { + type = list(string) +} + +variable "api_blocked_prefixes" { + type = list(string) +} + +variable "api_key" { + type = string +} + +variable "comment" { + type = string +} + +variable "enabled" { + type = bool +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/test.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/test.tf new file mode 100644 index 0000000000..ef388edb5d --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/test.tf @@ -0,0 +1,9 @@ +resource "snowflake_api_integration" "test_gcp_int" { + name = var.name + api_provider = "google_api_gateway" + google_audience = var.google_audience + api_allowed_prefixes = var.api_allowed_prefixes + api_blocked_prefixes = var.api_blocked_prefixes + comment = var.comment + enabled = var.enabled +} diff --git a/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/variables.tf b/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/variables.tf new file mode 100644 index 0000000000..0f8a0c6742 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ApiIntegration_google/1/variables.tf @@ -0,0 +1,24 @@ +variable "name" { + type = string +} + +variable "google_audience" { + type = string +} + +variable "api_allowed_prefixes" { + type = list(string) +} + +variable "api_blocked_prefixes" { + type = list(string) +} + +variable "comment" { + type = string +} + +variable "enabled" { + type = bool +} + diff --git a/pkg/sdk/api_integrations_def.go b/pkg/sdk/api_integrations_def.go index 043b96f11c..9625b23e72 100644 --- a/pkg/sdk/api_integrations_def.go +++ b/pkg/sdk/api_integrations_def.go @@ -83,17 +83,25 @@ var ApiIntegrationsDef = g.NewInterface( OptionalQueryStructField( "AzureParams", g.NewQueryStruct("SetAzureApiParams"). + OptionalTextAssignment("AZURE_TENANT_ID", g.ParameterOptions().SingleQuotes()). OptionalTextAssignment("AZURE_AD_APPLICATION_ID", g.ParameterOptions().SingleQuotes()). OptionalTextAssignment("API_KEY", g.ParameterOptions().SingleQuotes()). - WithValidation(g.AtLeastOneValueSet, "AzureAdApplicationId", "ApiKey"), + WithValidation(g.AtLeastOneValueSet, "AzureTenantId", "AzureAdApplicationId", "ApiKey"), + g.KeywordOptions(), + ). + OptionalQueryStructField( + "GoogleParams", + g.NewQueryStruct("SetGoogleApiParams"). + TextAssignment("GOOGLE_AUDIENCE", g.ParameterOptions().SingleQuotes().Required()), g.KeywordOptions(), ). OptionalBooleanAssignment("ENABLED", g.ParameterOptions()). ListAssignment("API_ALLOWED_PREFIXES", "ApiIntegrationEndpointPrefix", g.ParameterOptions().Parentheses()). ListAssignment("API_BLOCKED_PREFIXES", "ApiIntegrationEndpointPrefix", g.ParameterOptions().Parentheses()). OptionalComment(). - WithValidation(g.ConflictingFields, "AwsParams", "AzureParams"). - WithValidation(g.AtLeastOneValueSet, "AwsParams", "AzureParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment"), + // resulting validation changed to moreThanOneValueSet (not yet supported in the generator) + WithValidation(g.ConflictingFields, "AwsParams", "AzureParams", "GoogleParams"). + WithValidation(g.AtLeastOneValueSet, "AwsParams", "AzureParams", "GoogleParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment"), g.KeywordOptions().SQL("SET"), ). OptionalQueryStructField( diff --git a/pkg/sdk/api_integrations_dto_builders_gen.go b/pkg/sdk/api_integrations_dto_builders_gen.go index 90217bef3d..82d05298d7 100644 --- a/pkg/sdk/api_integrations_dto_builders_gen.go +++ b/pkg/sdk/api_integrations_dto_builders_gen.go @@ -136,6 +136,11 @@ func (s *ApiIntegrationSetRequest) WithAzureParams(AzureParams *SetAzureApiParam return s } +func (s *ApiIntegrationSetRequest) WithGoogleParams(GoogleParams *SetGoogleApiParamsRequest) *ApiIntegrationSetRequest { + s.GoogleParams = GoogleParams + return s +} + func (s *ApiIntegrationSetRequest) WithEnabled(Enabled *bool) *ApiIntegrationSetRequest { s.Enabled = Enabled return s @@ -174,6 +179,11 @@ func NewSetAzureApiParamsRequest() *SetAzureApiParamsRequest { return &SetAzureApiParamsRequest{} } +func (s *SetAzureApiParamsRequest) WithAzureTenantId(AzureTenantId *string) *SetAzureApiParamsRequest { + s.AzureTenantId = AzureTenantId + return s +} + func (s *SetAzureApiParamsRequest) WithAzureAdApplicationId(AzureAdApplicationId *string) *SetAzureApiParamsRequest { s.AzureAdApplicationId = AzureAdApplicationId return s @@ -184,6 +194,14 @@ func (s *SetAzureApiParamsRequest) WithApiKey(ApiKey *string) *SetAzureApiParams return s } +func NewSetGoogleApiParamsRequest( + GoogleAudience string, +) *SetGoogleApiParamsRequest { + s := SetGoogleApiParamsRequest{} + s.GoogleAudience = GoogleAudience + return &s +} + func NewApiIntegrationUnsetRequest() *ApiIntegrationUnsetRequest { return &ApiIntegrationUnsetRequest{} } diff --git a/pkg/sdk/api_integrations_dto_gen.go b/pkg/sdk/api_integrations_dto_gen.go index d0ca623dd8..5ae6091fac 100644 --- a/pkg/sdk/api_integrations_dto_gen.go +++ b/pkg/sdk/api_integrations_dto_gen.go @@ -55,6 +55,7 @@ type AlterApiIntegrationRequest struct { type ApiIntegrationSetRequest struct { AwsParams *SetAwsApiParamsRequest AzureParams *SetAzureApiParamsRequest + GoogleParams *SetGoogleApiParamsRequest Enabled *bool ApiAllowedPrefixes []ApiIntegrationEndpointPrefix ApiBlockedPrefixes []ApiIntegrationEndpointPrefix @@ -67,10 +68,15 @@ type SetAwsApiParamsRequest struct { } type SetAzureApiParamsRequest struct { + AzureTenantId *string AzureAdApplicationId *string ApiKey *string } +type SetGoogleApiParamsRequest struct { + GoogleAudience string // required +} + type ApiIntegrationUnsetRequest struct { ApiKey *bool Enabled *bool diff --git a/pkg/sdk/api_integrations_gen.go b/pkg/sdk/api_integrations_gen.go index 69cda52d1d..7d85c5286b 100644 --- a/pkg/sdk/api_integrations_gen.go +++ b/pkg/sdk/api_integrations_gen.go @@ -68,6 +68,7 @@ type AlterApiIntegrationOptions struct { type ApiIntegrationSet struct { AwsParams *SetAwsApiParams `ddl:"keyword"` AzureParams *SetAzureApiParams `ddl:"keyword"` + GoogleParams *SetGoogleApiParams `ddl:"keyword"` Enabled *bool `ddl:"parameter" sql:"ENABLED"` ApiAllowedPrefixes []ApiIntegrationEndpointPrefix `ddl:"parameter,parentheses" sql:"API_ALLOWED_PREFIXES"` ApiBlockedPrefixes []ApiIntegrationEndpointPrefix `ddl:"parameter,parentheses" sql:"API_BLOCKED_PREFIXES"` @@ -80,10 +81,15 @@ type SetAwsApiParams struct { } type SetAzureApiParams struct { + AzureTenantId *string `ddl:"parameter,single_quotes" sql:"AZURE_TENANT_ID"` AzureAdApplicationId *string `ddl:"parameter,single_quotes" sql:"AZURE_AD_APPLICATION_ID"` ApiKey *string `ddl:"parameter,single_quotes" sql:"API_KEY"` } +type SetGoogleApiParams struct { + GoogleAudience string `ddl:"parameter,single_quotes" sql:"GOOGLE_AUDIENCE"` +} + type ApiIntegrationUnset struct { ApiKey *bool `ddl:"keyword" sql:"API_KEY"` Enabled *bool `ddl:"keyword" sql:"ENABLED"` diff --git a/pkg/sdk/api_integrations_gen_test.go b/pkg/sdk/api_integrations_gen_test.go index 5e3be6e53a..df504d2cb6 100644 --- a/pkg/sdk/api_integrations_gen_test.go +++ b/pkg/sdk/api_integrations_gen_test.go @@ -180,19 +180,19 @@ func TestApiIntegrations_Alter(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterApiIntegrationOptions", "Set", "Unset", "SetTags", "UnsetTags")) }) - t.Run("validation: conflicting fields for [opts.Set.AwsParams opts.Set.AzureParams]", func(t *testing.T) { + t.Run("validation: conflicting fields for [opts.Set.AwsParams opts.Set.AzureParams opts.Set.GoogleParams]", func(t *testing.T) { opts := defaultOpts() opts.Set = &ApiIntegrationSet{ AwsParams: &SetAwsApiParams{ApiKey: String("key")}, AzureParams: &SetAzureApiParams{ApiKey: String("key")}, } - assertOptsInvalidJoinedErrors(t, opts, errOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams")) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "GoogleParams")) }) - t.Run("validation: at least one of the fields [opts.Set.AwsParams opts.Set.AzureParams opts.Set.Enabled opts.Set.ApiAllowedPrefixes opts.Set.ApiBlockedPrefixes opts.Set.Comment] should be set", func(t *testing.T) { + t.Run("validation: at least one of the fields [opts.Set.AwsParams opts.Set.AzureParams opts.Set.GoogleParams opts.Set.Enabled opts.Set.ApiAllowedPrefixes opts.Set.ApiBlockedPrefixes opts.Set.Comment] should be set", func(t *testing.T) { opts := defaultOpts() opts.Set = &ApiIntegrationSet{} - assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment")) + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "GoogleParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment")) }) t.Run("validation: at least one of the fields [opts.Set.AwsParams.ApiAwsRoleArn opts.Set.AwsParams.ApiKey] should be set", func(t *testing.T) { @@ -203,12 +203,12 @@ func TestApiIntegrations_Alter(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterApiIntegrationOptions.Set.AwsParams", "ApiAwsRoleArn", "ApiKey")) }) - t.Run("validation: at least one of the fields [opts.Set.AzureParams.AzureAdApplicationId opts.Set.AzureParams.ApiKey] should be set", func(t *testing.T) { + t.Run("validation: at least one of the fields [opts.Set.AzureParams.AzureTenantId opts.Set.AzureParams.AzureAdApplicationId opts.Set.AzureParams.ApiKey] should be set", func(t *testing.T) { opts := defaultOpts() opts.Set = &ApiIntegrationSet{ AzureParams: &SetAzureApiParams{}, } - assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterApiIntegrationOptions.Set.AzureParams", "AzureAdApplicationId", "ApiKey")) + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterApiIntegrationOptions.Set.AzureParams", "AzureTenantId", "AzureAdApplicationId", "ApiKey")) }) t.Run("validation: at least one of the fields [opts.Unset.ApiKey opts.Unset.Enabled opts.Unset.ApiBlockedPrefixes opts.Unset.Comment] should be set", func(t *testing.T) { diff --git a/pkg/sdk/api_integrations_impl_gen.go b/pkg/sdk/api_integrations_impl_gen.go index e93cdd9588..7968228246 100644 --- a/pkg/sdk/api_integrations_impl_gen.go +++ b/pkg/sdk/api_integrations_impl_gen.go @@ -114,10 +114,16 @@ func (r *AlterApiIntegrationRequest) toOpts() *AlterApiIntegrationOptions { } if r.Set.AzureParams != nil { opts.Set.AzureParams = &SetAzureApiParams{ + AzureTenantId: r.Set.AzureParams.AzureTenantId, AzureAdApplicationId: r.Set.AzureParams.AzureAdApplicationId, ApiKey: r.Set.AzureParams.ApiKey, } } + if r.Set.GoogleParams != nil { + opts.Set.GoogleParams = &SetGoogleApiParams{ + GoogleAudience: r.Set.GoogleParams.GoogleAudience, + } + } } if r.Unset != nil { opts.Unset = &ApiIntegrationUnset{ diff --git a/pkg/sdk/api_integrations_validations_gen.go b/pkg/sdk/api_integrations_validations_gen.go index 092327f553..64e01eaac5 100644 --- a/pkg/sdk/api_integrations_validations_gen.go +++ b/pkg/sdk/api_integrations_validations_gen.go @@ -43,11 +43,11 @@ func (opts *AlterApiIntegrationOptions) validate() error { errs = append(errs, errExactlyOneOf("AlterApiIntegrationOptions", "Set", "Unset", "SetTags", "UnsetTags")) } if valueSet(opts.Set) { - if everyValueSet(opts.Set.AwsParams, opts.Set.AzureParams) { - errs = append(errs, errOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams")) + if moreThanOneValueSet(opts.Set.AwsParams, opts.Set.AzureParams, opts.Set.GoogleParams) { + errs = append(errs, errMoreThanOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "GoogleParams")) } - if !anyValueSet(opts.Set.AwsParams, opts.Set.AzureParams, opts.Set.Enabled, opts.Set.ApiAllowedPrefixes, opts.Set.ApiBlockedPrefixes, opts.Set.Comment) { - errs = append(errs, errAtLeastOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment")) + if !anyValueSet(opts.Set.AwsParams, opts.Set.AzureParams, opts.Set.GoogleParams, opts.Set.Enabled, opts.Set.ApiAllowedPrefixes, opts.Set.ApiBlockedPrefixes, opts.Set.Comment) { + errs = append(errs, errAtLeastOneOf("AlterApiIntegrationOptions.Set", "AwsParams", "AzureParams", "GoogleParams", "Enabled", "ApiAllowedPrefixes", "ApiBlockedPrefixes", "Comment")) } if valueSet(opts.Set.AwsParams) { if !anyValueSet(opts.Set.AwsParams.ApiAwsRoleArn, opts.Set.AwsParams.ApiKey) { @@ -55,8 +55,8 @@ func (opts *AlterApiIntegrationOptions) validate() error { } } if valueSet(opts.Set.AzureParams) { - if !anyValueSet(opts.Set.AzureParams.AzureAdApplicationId, opts.Set.AzureParams.ApiKey) { - errs = append(errs, errAtLeastOneOf("AlterApiIntegrationOptions.Set.AzureParams", "AzureAdApplicationId", "ApiKey")) + if !anyValueSet(opts.Set.AzureParams.AzureTenantId, opts.Set.AzureParams.AzureAdApplicationId, opts.Set.AzureParams.ApiKey) { + errs = append(errs, errAtLeastOneOf("AlterApiIntegrationOptions.Set.AzureParams", "AzureTenantId", "AzureAdApplicationId", "ApiKey")) } } } diff --git a/pkg/sdk/testint/api_integrations_gen_integration_test.go b/pkg/sdk/testint/api_integrations_gen_integration_test.go index 39325f3580..3c38d29848 100644 --- a/pkg/sdk/testint/api_integrations_gen_integration_test.go +++ b/pkg/sdk/testint/api_integrations_gen_integration_test.go @@ -23,8 +23,10 @@ func TestInt_ApiIntegrations(t *testing.T) { const googleOtherPrefix = "https://gateway-id-123456.uc.gateway.dev/dev" const apiAwsRoleArn = "arn:aws:iam::000000000001:/role/test" const azureTenantId = "00000000-0000-0000-0000-000000000000" + const azureOtherTenantId = "11111111-1111-1111-1111-111111111111" const azureAdApplicationId = "11111111-1111-1111-1111-111111111111" const googleAudience = "api-gateway-id-123456.apigateway.gcp-project.cloud.goog" + const googleOtherAudience = "api-gateway-id-666777.apigateway.gcp-project.cloud.goog" prefixes := func(prefix string) []sdk.ApiIntegrationEndpointPrefix { return []sdk.ApiIntegrationEndpointPrefix{{Path: prefix}} @@ -275,6 +277,20 @@ func TestInt_ApiIntegrations(t *testing.T) { assert.Contains(t, details, sdk.ApiIntegrationProperty{Name: "COMMENT", Type: "String", Value: "", Default: ""}) }) + t.Run("alter api integration: azure - missing option", func(t *testing.T) { + integration := createAzureApiIntegration(t) + + setRequest := sdk.NewAlterApiIntegrationRequest(integration.ID()). + WithSet(sdk.NewApiIntegrationSetRequest().WithAzureParams(sdk.NewSetAzureApiParamsRequest().WithAzureTenantId(sdk.String(azureOtherTenantId)))) + err := client.ApiIntegrations.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.ApiIntegrations.Describe(ctx, integration.ID()) + require.NoError(t, err) + + assert.Contains(t, details, sdk.ApiIntegrationProperty{Name: "AZURE_TENANT_ID", Type: "String", Value: azureOtherTenantId, Default: ""}) + }) + t.Run("alter api integration: google", func(t *testing.T) { integration := createGoogleApiIntegration(t) @@ -317,6 +333,20 @@ func TestInt_ApiIntegrations(t *testing.T) { assert.Contains(t, details, sdk.ApiIntegrationProperty{Name: "COMMENT", Type: "String", Value: "", Default: ""}) }) + t.Run("alter api integration: google - missing option", func(t *testing.T) { + integration := createGoogleApiIntegration(t) + + setRequest := sdk.NewAlterApiIntegrationRequest(integration.ID()). + WithSet(sdk.NewApiIntegrationSetRequest().WithGoogleParams(sdk.NewSetGoogleApiParamsRequest(googleOtherAudience))) + err := client.ApiIntegrations.Alter(ctx, setRequest) + require.NoError(t, err) + + details, err := client.ApiIntegrations.Describe(ctx, integration.ID()) + require.NoError(t, err) + + assert.Contains(t, details, sdk.ApiIntegrationProperty{Name: "GOOGLE_AUDIENCE", Type: "String", Value: googleOtherAudience, Default: ""}) + }) + t.Run("alter api integration: set and unset tags", func(t *testing.T) { tag, tagCleanup := createTag(t, client, testDb(t), testSchema(t)) t.Cleanup(tagCleanup) diff --git a/pkg/snowflake/api_integration.go b/pkg/snowflake/api_integration.go deleted file mode 100644 index 11ff2d6c71..0000000000 --- a/pkg/snowflake/api_integration.go +++ /dev/null @@ -1,39 +0,0 @@ -package snowflake - -import ( - "database/sql" - - "github.com/jmoiron/sqlx" -) - -// ApiIntegration returns a pointer to a Builder that abstracts the DDL operations for an api integration. -// -// Supported DDL operations are: -// - CREATE API INTEGRATION -// - ALTER API INTEGRATION -// - DROP INTEGRATION -// - SHOW INTEGRATIONS -// - DESCRIBE INTEGRATION -// -// [Snowflake Reference](https://docs.snowflake.com/en/sql-reference/ddl-user-security.html#api-integrations) -func NewAPIIntegrationBuilder(name string) *Builder { - return &Builder{ - entityType: APIIntegrationType, - name: name, - } -} - -type APIIntegration struct { - Name sql.NullString `db:"name"` - Category sql.NullString `db:"category"` - IntegrationType sql.NullString `db:"type"` - CreatedOn sql.NullString `db:"created_on"` - Comment sql.NullString `db:"comment"` - Enabled sql.NullBool `db:"enabled"` -} - -func ScanAPIIntegration(row *sqlx.Row) (*APIIntegration, error) { - r := &APIIntegration{} - err := row.StructScan(r) - return r, err -} diff --git a/pkg/snowflake/api_integration_test.go b/pkg/snowflake/api_integration_test.go deleted file mode 100644 index e6b726f695..0000000000 --- a/pkg/snowflake/api_integration_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package snowflake_test - -import ( - "testing" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" - "github.com/stretchr/testify/require" -) - -func TestAPIIntegration(t *testing.T) { - r := require.New(t) - builder := snowflake.NewAPIIntegrationBuilder("aws_api") - r.NotNil(builder) - - q := builder.Show() - r.Equal("SHOW API INTEGRATIONS LIKE 'aws_api'", q) - - c := builder.Create() - - c.SetRaw(`API_PROVIDER=aws_private_api_gateway`) - c.SetString(`api_aws_role_arn`, "arn:aws:iam::xxxx:role/snowflake-execute-externalfunc-privendpoint-role") - c.SetStringList(`api_allowed_prefixes`, []string{"https://123456.execute-api.us-west-2.amazonaws.com/prod/", "https://123456.execute-api.us-west-2.amazonaws.com/test/"}) - c.SetBool(`enabled`, true) - q = c.Statement() - - r.Equal(`CREATE API INTEGRATION "aws_api" API_PROVIDER=aws_private_api_gateway API_AWS_ROLE_ARN='arn:aws:iam::xxxx:role/snowflake-execute-externalfunc-privendpoint-role' API_ALLOWED_PREFIXES=('https://123456.execute-api.us-west-2.amazonaws.com/prod/', 'https://123456.execute-api.us-west-2.amazonaws.com/test/') ENABLED=true`, q) -}