Skip to content

Commit

Permalink
feat: Add identifier with arguments (#2979)
Browse files Browse the repository at this point in the history
## Changes
- Add a new identifier type that supports arguments
- Adjust the function's SDK/resource/data source implementation and
tests, so that it uses the new identifier

note: Create signature wasn't changed, because it contains more info
like argument name or default value.

## TODOs
- Remove additional validation from ParseIdentifierString
- Another function identifier representation to parse (SHOW GRANTS)

## Next pr(s)
- Use in procedure and external function (SDK/resources/data sources)
- Remove arguments from SchemaObjectIdentifier (and all helper functions
regarding SchemaObjectIdentifier with arguments)
  • Loading branch information
sfc-gh-jcieslak authored Aug 8, 2024
1 parent 3bae7f6 commit 00ae1c5
Show file tree
Hide file tree
Showing 31 changed files with 1,010 additions and 575 deletions.
19 changes: 11 additions & 8 deletions pkg/acceptance/check_destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ func CheckDestroy(t *testing.T, resource resources.Resource) func(*terraform.Sta
}
t.Logf("found resource %s in state", resource)
ctx := context.Background()
id := decodeSnowflakeId(rs, resource)
id, err := decodeSnowflakeId(rs, resource)
if err != nil {
return err
}
if id == nil {
return fmt.Errorf("could not get the id of %s", resource)
}
Expand All @@ -45,16 +48,16 @@ func CheckDestroy(t *testing.T, resource resources.Resource) func(*terraform.Sta
}
}

func decodeSnowflakeId(rs *terraform.ResourceState, resource resources.Resource) sdk.ObjectIdentifier {
func decodeSnowflakeId(rs *terraform.ResourceState, resource resources.Resource) (sdk.ObjectIdentifier, error) {
switch resource {
case resources.ExternalFunction:
return sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(rs.Primary.ID)
return sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(rs.Primary.ID), nil
case resources.Function:
return sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(rs.Primary.ID)
return sdk.ParseSchemaObjectIdentifierWithArguments(rs.Primary.ID)
case resources.Procedure:
return sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(rs.Primary.ID)
return sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(rs.Primary.ID), nil
default:
return helpers.DecodeSnowflakeID(rs.Primary.ID)
return helpers.DecodeSnowflakeID(rs.Primary.ID), nil
}
}

Expand Down Expand Up @@ -213,7 +216,7 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{
},
}

func runShowById[T any, U sdk.AccountObjectIdentifier | sdk.DatabaseObjectIdentifier | sdk.SchemaObjectIdentifier | sdk.TableColumnIdentifier](ctx context.Context, id sdk.ObjectIdentifier, show func(ctx context.Context, id U) (T, error)) error {
func runShowById[T any, U sdk.AccountObjectIdentifier | sdk.DatabaseObjectIdentifier | sdk.SchemaObjectIdentifier | sdk.TableColumnIdentifier | sdk.SchemaObjectIdentifierWithArguments](ctx context.Context, id sdk.ObjectIdentifier, show func(ctx context.Context, id U) (T, error)) error {
idCast, err := asId[U](id)
if err != nil {
return err
Expand All @@ -222,7 +225,7 @@ func runShowById[T any, U sdk.AccountObjectIdentifier | sdk.DatabaseObjectIdenti
return err
}

func asId[T sdk.AccountObjectIdentifier | sdk.DatabaseObjectIdentifier | sdk.SchemaObjectIdentifier | sdk.TableColumnIdentifier](id sdk.ObjectIdentifier) (*T, error) {
func asId[T sdk.AccountObjectIdentifier | sdk.DatabaseObjectIdentifier | sdk.SchemaObjectIdentifier | sdk.TableColumnIdentifier | sdk.SchemaObjectIdentifierWithArguments](id sdk.ObjectIdentifier) (*T, error) {
if idCast, ok := id.(T); !ok {
return nil, fmt.Errorf("expected %s identifier type, but got: %T", reflect.TypeOf(new(T)).Elem().Name(), id)
} else {
Expand Down
20 changes: 16 additions & 4 deletions pkg/acceptance/helpers/ids_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,26 @@ func (c *IdsGenerator) RandomSchemaObjectIdentifierWithPrefix(prefix string) sdk
return sdk.NewSchemaObjectIdentifierInSchema(c.SchemaId(), c.AlphaWithPrefix(prefix))
}

func (c *IdsGenerator) RandomSchemaObjectIdentifierWithArguments(arguments []sdk.DataType) sdk.SchemaObjectIdentifier {
return sdk.NewSchemaObjectIdentifierWithArguments(c.SchemaId().DatabaseName(), c.SchemaId().Name(), c.Alpha(), arguments)
}

func (c *IdsGenerator) RandomSchemaObjectIdentifierInSchema(schemaId sdk.DatabaseObjectIdentifier) sdk.SchemaObjectIdentifier {
return sdk.NewSchemaObjectIdentifierInSchema(schemaId, c.Alpha())
}

func (c *IdsGenerator) RandomSchemaObjectIdentifierWithArgumentsOld(arguments ...sdk.DataType) sdk.SchemaObjectIdentifier {
return sdk.NewSchemaObjectIdentifierWithArgumentsOld(c.SchemaId().DatabaseName(), c.SchemaId().Name(), c.Alpha(), arguments)
}

func (c *IdsGenerator) NewSchemaObjectIdentifierWithArguments(name string, arguments ...sdk.DataType) sdk.SchemaObjectIdentifierWithArguments {
return sdk.NewSchemaObjectIdentifierWithArguments(c.SchemaId().DatabaseName(), c.SchemaId().Name(), name, arguments...)
}

func (c *IdsGenerator) NewSchemaObjectIdentifierWithArgumentsInSchema(name string, schemaId sdk.DatabaseObjectIdentifier, argumentDataTypes ...sdk.DataType) sdk.SchemaObjectIdentifierWithArguments {
return sdk.NewSchemaObjectIdentifierWithArgumentsInSchema(schemaId, name, argumentDataTypes...)
}

func (c *IdsGenerator) RandomSchemaObjectIdentifierWithArguments(arguments ...sdk.DataType) sdk.SchemaObjectIdentifierWithArguments {
return sdk.NewSchemaObjectIdentifierWithArguments(c.SchemaId().DatabaseName(), c.SchemaId().Name(), c.Alpha(), arguments...)
}

func (c *IdsGenerator) Alpha() string {
return c.AlphaN(6)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/datasources/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func ReadContextFunctions(ctx context.Context, d *schema.ResourceData, meta inte
schemaName := d.Get("schema").(string)

request := sdk.NewShowFunctionRequest()
request.WithIn(&sdk.In{Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)})
request.WithIn(sdk.In{Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)})
functions, err := client.Functions.Show(ctx, request)
if err != nil {
id := d.Id()
Expand All @@ -92,7 +92,8 @@ func ReadContextFunctions(ctx context.Context, d *schema.ResourceData, meta inte

entities := []map[string]interface{}{}
for _, item := range functions {
signature, err := parseArguments(item.Arguments)
// TODO(SNOW-1596962): Create argument parsing function that takes argument names into consideration.
signature, err := parseArguments(item.ArgumentsRaw)
if err != nil {
return diag.FromErr(err)
}
Expand Down
7 changes: 3 additions & 4 deletions pkg/resources/external_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"strings"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -318,7 +317,7 @@ func CreateContextExternalFunction(ctx context.Context, d *schema.ResourceData,
for _, item := range args {
argTypes = append(argTypes, item.ArgDataType)
}
sid := sdk.NewSchemaObjectIdentifierWithArguments(database, schemaName, name, argTypes)
sid := sdk.NewSchemaObjectIdentifierWithArgumentsOld(database, schemaName, name, argTypes)
d.SetId(sid.FullyQualifiedName())
return ReadContextExternalFunction(ctx, d, meta)
}
Expand Down Expand Up @@ -476,7 +475,7 @@ func UpdateContextExternalFunction(ctx context.Context, d *schema.ResourceData,
client := meta.(*provider.Context).Client

id := sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(d.Id())
req := sdk.NewAlterFunctionRequest(id.WithoutArguments(), id.Arguments())
req := sdk.NewAlterFunctionRequest(sdk.NewSchemaObjectIdentifierWithArguments(id.DatabaseName(), id.SchemaName(), id.Name(), id.Arguments()...))
if d.HasChange("comment") {
_, new := d.GetChange("comment")
if new == "" {
Expand All @@ -496,7 +495,7 @@ func DeleteContextExternalFunction(ctx context.Context, d *schema.ResourceData,
client := meta.(*provider.Context).Client

id := sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(d.Id())
req := sdk.NewDropFunctionRequest(id.WithoutArguments(), id.Arguments())
req := sdk.NewDropFunctionRequest(sdk.NewSchemaObjectIdentifierWithArguments(id.DatabaseName(), id.SchemaName(), id.Name(), id.Arguments()...))
if err := client.Functions.Drop(ctx, req); err != nil {
return diag.FromErr(err)
}
Expand Down
151 changes: 75 additions & 76 deletions pkg/resources/external_function_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"testing"

acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/hashicorp/terraform-plugin-testing/config"
Expand Down Expand Up @@ -226,7 +225,7 @@ func TestAcc_ExternalFunction_complete(t *testing.T) {
}

func TestAcc_ExternalFunction_migrateFromVersion085(t *testing.T) {
id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArguments([]sdk.DataType{sdk.DataTypeVARCHAR, sdk.DataTypeVARCHAR})
id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsOld(sdk.DataTypeVARCHAR, sdk.DataTypeVARCHAR)
name := id.Name()
resourceName := "snowflake_external_function.f"

Expand Down Expand Up @@ -450,30 +449,30 @@ func externalFunctionConfigWithReturnNullAllowed(database string, schema string,

return fmt.Sprintf(`
resource "snowflake_api_integration" "test_api_int" {
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
}
resource "snowflake_external_function" "f" {
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "ARG1"
type = "VARCHAR"
}
arg {
name = "ARG2"
type = "VARCHAR"
}
return_type = "VARIANT"
return_behavior = "IMMUTABLE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
%[4]s
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "ARG1"
type = "VARCHAR"
}
arg {
name = "ARG2"
type = "VARCHAR"
}
return_type = "VARIANT"
return_behavior = "IMMUTABLE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
%[4]s
}
`, database, schema, name, returnNullAllowedText)
Expand All @@ -482,80 +481,80 @@ resource "snowflake_external_function" "f" {
func externalFunctionConfigIssue2528(database string, schema string, name string, schema2 string) string {
return fmt.Sprintf(`
resource "snowflake_api_integration" "test_api_int" {
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
}
resource "snowflake_schema" "s2" {
database = "%[1]s"
name = "%[4]s"
database = "%[1]s"
name = "%[4]s"
}
resource "snowflake_external_function" "f" {
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "SNS_NOTIF"
type = "OBJECT"
}
return_type = "VARIANT"
return_behavior = "VOLATILE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "SNS_NOTIF"
type = "OBJECT"
}
return_type = "VARIANT"
return_behavior = "VOLATILE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
}
resource "snowflake_external_function" "f2" {
depends_on = [snowflake_schema.s2]
name = "%[3]s"
database = "%[1]s"
schema = "%[4]s"
arg {
name = "SNS_NOTIF"
type = "OBJECT"
}
return_type = "VARIANT"
return_behavior = "VOLATILE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
depends_on = [snowflake_schema.s2]
name = "%[3]s"
database = "%[1]s"
schema = "%[4]s"
arg {
name = "SNS_NOTIF"
type = "OBJECT"
}
return_type = "VARIANT"
return_behavior = "VOLATILE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
}
`, database, schema, name, schema2)
}

func externalFunctionConfigIssueCurlyHeader(id sdk.SchemaObjectIdentifier) string {
return fmt.Sprintf(`
resource "snowflake_api_integration" "test_api_int" {
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
name = "%[3]s"
api_provider = "aws_api_gateway"
api_aws_role_arn = "arn:aws:iam::000000000001:/role/test"
api_allowed_prefixes = ["https://123456.execute-api.us-west-2.amazonaws.com/prod/"]
enabled = true
}
resource "snowflake_external_function" "f" {
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "ARG1"
type = "VARCHAR"
}
arg {
name = "ARG2"
type = "VARCHAR"
}
header {
name = "%[3]s"
database = "%[1]s"
schema = "%[2]s"
arg {
name = "ARG1"
type = "VARCHAR"
}
arg {
name = "ARG2"
type = "VARCHAR"
}
header {
name = "name"
value = "{0}"
}
return_type = "VARIANT"
return_behavior = "IMMUTABLE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
}
return_type = "VARIANT"
return_behavior = "IMMUTABLE"
api_integration = snowflake_api_integration.test_api_int.name
url_of_proxy_and_resource = "https://123456.execute-api.us-west-2.amazonaws.com/prod/test_func"
}
`, id.DatabaseName(), id.SchemaName(), id.Name())
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/external_function_state_upgraders.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func v085ExternalFunctionStateUpgrader(ctx context.Context, rawState map[string]
}
}

schemaObjectIdentifierWithArguments := sdk.NewSchemaObjectIdentifierWithArguments(parsedV085ExternalFunctionId.DatabaseName, parsedV085ExternalFunctionId.SchemaName, parsedV085ExternalFunctionId.ExternalFunctionName, argDataTypes)
schemaObjectIdentifierWithArguments := sdk.NewSchemaObjectIdentifierWithArgumentsOld(parsedV085ExternalFunctionId.DatabaseName, parsedV085ExternalFunctionId.SchemaName, parsedV085ExternalFunctionId.ExternalFunctionName, argDataTypes)
rawState["id"] = schemaObjectIdentifierWithArguments.FullyQualifiedName()

oldDatabase := rawState["database"].(string)
Expand Down
Loading

0 comments on commit 00ae1c5

Please sign in to comment.