Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Use new identifier with arguments in function, external function and procedure grants #3002

Merged
merged 21 commits into from
Aug 20, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
changes after review
sfc-gh-jcieslak committed Aug 13, 2024
commit d4b2bb298c153866a8572e67064eba519f85008e
56 changes: 56 additions & 0 deletions pkg/acceptance/helpers/external_function_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package helpers

import (
"context"

Check failure on line 4 in pkg/acceptance/helpers/external_function_client.go

GitHub Actions / reviewdog

[golangci] reported by reviewdog 🐶 File is not `gofumpt`-ed (gofumpt) Raw Output: pkg/acceptance/helpers/external_function_client.go:4: File is not `gofumpt`-ed (gofumpt) "context"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/stretchr/testify/require"
"testing"

Check failure on line 7 in pkg/acceptance/helpers/external_function_client.go

GitHub Actions / reviewdog

[golangci] reported by reviewdog 🐶 File is not `gofumpt`-ed (gofumpt) Raw Output: pkg/acceptance/helpers/external_function_client.go:7: File is not `gofumpt`-ed (gofumpt) "testing"
)

type ExternalFunctionClient struct {
context *TestClientContext
ids *IdsGenerator
}

func NewExternalFunctionClient(context *TestClientContext, idsGenerator *IdsGenerator) *ExternalFunctionClient {
return &ExternalFunctionClient{
context: context,
ids: idsGenerator,
}
}

func (c *ExternalFunctionClient) client() sdk.ExternalFunctions {
return c.context.client.ExternalFunctions
}

func (c *ExternalFunctionClient) Create(t *testing.T, apiIntegrationId sdk.AccountObjectIdentifier, arguments ...sdk.DataType) *sdk.ExternalFunction {
t.Helper()
return c.CreateWithIdentifier(t, apiIntegrationId, c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...))
}

func (c *ExternalFunctionClient) CreateWithIdentifier(t *testing.T, apiIntegrationId sdk.AccountObjectIdentifier, id sdk.SchemaObjectIdentifierWithArguments) *sdk.ExternalFunction {
t.Helper()
ctx := context.Background()
argumentRequests := make([]sdk.ExternalFunctionArgumentRequest, len(id.ArgumentDataTypes()))
for i, argumentDataType := range id.ArgumentDataTypes() {
argumentRequests[i] = *sdk.NewExternalFunctionArgumentRequest(c.ids.Alpha(), argumentDataType)
}
err := c.client().Create(ctx,
sdk.NewCreateExternalFunctionRequest(
id.SchemaObjectId(),
sdk.DataTypeVariant,
&apiIntegrationId,
"https://xyz.execute-api.us-west-2.amazonaws.com/production/remote_echo",
).WithArguments(argumentRequests),
)
require.NoError(t, err)

t.Cleanup(func() {
require.NoError(t, c.context.client.Functions.Drop(ctx, sdk.NewDropFunctionRequest(id).WithIfExists(true)))
})

externalFunction, err := c.client().ShowByID(ctx, id)
require.NoError(t, err)

return externalFunction
}
55 changes: 55 additions & 0 deletions pkg/acceptance/helpers/function_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package helpers

import (
"context"

Check failure on line 4 in pkg/acceptance/helpers/function_client.go

GitHub Actions / reviewdog

[golangci] reported by reviewdog 🐶 File is not `gofumpt`-ed (gofumpt) Raw Output: pkg/acceptance/helpers/function_client.go:4: File is not `gofumpt`-ed (gofumpt) "context"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/stretchr/testify/require"
"testing"
)

type FunctionClient struct {
context *TestClientContext
ids *IdsGenerator
}

func NewFunctionClient(context *TestClientContext, idsGenerator *IdsGenerator) *FunctionClient {
return &FunctionClient{
context: context,
ids: idsGenerator,
}
}

func (c *FunctionClient) client() sdk.Functions {
return c.context.client.Functions
}

func (c *FunctionClient) Create(t *testing.T, arguments ...sdk.DataType) *sdk.Function {
t.Helper()
return c.CreateWithIdentifier(t, c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...))
}

func (c *FunctionClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) *sdk.Function {
t.Helper()
ctx := context.Background()
argumentRequests := make([]sdk.FunctionArgumentRequest, len(id.ArgumentDataTypes()))
for i, argumentDataType := range id.ArgumentDataTypes() {
argumentRequests[i] = *sdk.NewFunctionArgumentRequest(c.ids.Alpha(), argumentDataType)
}
err := c.client().CreateForSQL(ctx,
sdk.NewCreateForSQLFunctionRequest(
id.SchemaObjectId(),
*sdk.NewFunctionReturnsRequest().WithResultDataType(*sdk.NewFunctionReturnsResultDataTypeRequest(sdk.DataTypeInt)),
"SELECT 1",
).WithArguments(argumentRequests),
)
require.NoError(t, err)

t.Cleanup(func() {
require.NoError(t, c.context.client.Functions.Drop(ctx, sdk.NewDropFunctionRequest(id).WithIfExists(true)))
})

function, err := c.client().ShowByID(ctx, id)
require.NoError(t, err)

return function
}
55 changes: 55 additions & 0 deletions pkg/acceptance/helpers/procedure_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package helpers

import (
"context"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/stretchr/testify/require"
"testing"
)

type ProcedureClient struct {
context *TestClientContext
ids *IdsGenerator
}

func NewProcedureClient(context *TestClientContext, idsGenerator *IdsGenerator) *ProcedureClient {
return &ProcedureClient{
context: context,
ids: idsGenerator,
}
}

func (c *ProcedureClient) client() sdk.Procedures {
return c.context.client.Procedures
}

func (c *ProcedureClient) Create(t *testing.T, arguments ...sdk.DataType) *sdk.Procedure {
t.Helper()
return c.CreateWithIdentifier(t, c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...))
}

func (c *ProcedureClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) *sdk.Procedure {
t.Helper()
ctx := context.Background()
argumentRequests := make([]sdk.ProcedureArgumentRequest, len(id.ArgumentDataTypes()))
for i, argumentDataType := range id.ArgumentDataTypes() {
argumentRequests[i] = *sdk.NewProcedureArgumentRequest(c.ids.Alpha(), argumentDataType)
}
err := c.client().CreateForSQL(ctx,
sdk.NewCreateForSQLProcedureRequest(
id.SchemaObjectId(),
*sdk.NewProcedureSQLReturnsRequest().WithResultDataType(*sdk.NewProcedureReturnsResultDataTypeRequest(sdk.DataTypeInt)),
"SELECT 1",
).WithArguments(argumentRequests),
)
require.NoError(t, err)

t.Cleanup(func() {
require.NoError(t, c.context.client.Procedures.Drop(ctx, sdk.NewDropProcedureRequest(id).WithIfExists(true)))
})

procedure, err := c.client().ShowByID(ctx, id)
require.NoError(t, err)

return procedure
}
6 changes: 6 additions & 0 deletions pkg/acceptance/helpers/test_client.go
Original file line number Diff line number Diff line change
@@ -24,9 +24,11 @@ type TestClient struct {
DataMetricFunctionReferences *DataMetricFunctionReferencesClient
DynamicTable *DynamicTableClient
ExternalAccessIntegration *ExternalAccessIntegrationClient
ExternalFunction *ExternalFunctionClient
ExternalVolume *ExternalVolumeClient
FailoverGroup *FailoverGroupClient
FileFormat *FileFormatClient
Function *FunctionClient
Grant *GrantClient
MaskingPolicy *MaskingPolicyClient
MaterializedView *MaterializedViewClient
@@ -35,6 +37,7 @@ type TestClient struct {
Parameter *ParameterClient
PasswordPolicy *PasswordPolicyClient
Pipe *PipeClient
Procedure *ProcedureClient
ProjectionPolicy *ProjectionPolicyClient
PolicyReferences *PolicyReferencesClient
ResourceMonitor *ResourceMonitorClient
@@ -83,9 +86,11 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri
DataMetricFunctionReferences: NewDataMetricFunctionReferencesClient(context),
DynamicTable: NewDynamicTableClient(context, idsGenerator),
ExternalAccessIntegration: NewExternalAccessIntegrationClient(context, idsGenerator),
ExternalFunction: NewExternalFunctionClient(context, idsGenerator),
ExternalVolume: NewExternalVolumeClient(context, idsGenerator),
FailoverGroup: NewFailoverGroupClient(context, idsGenerator),
FileFormat: NewFileFormatClient(context, idsGenerator),
Function: NewFunctionClient(context, idsGenerator),
Grant: NewGrantClient(context, idsGenerator),
MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator),
MaterializedView: NewMaterializedViewClient(context, idsGenerator),
@@ -94,6 +99,7 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri
Parameter: NewParameterClient(context),
PasswordPolicy: NewPasswordPolicyClient(context, idsGenerator),
Pipe: NewPipeClient(context, idsGenerator),
Procedure: NewProcedureClient(context, idsGenerator),
ProjectionPolicy: NewProjectionPolicyClient(context, idsGenerator),
PolicyReferences: NewPolicyReferencesClient(context),
ResourceMonitor: NewResourceMonitorClient(context, idsGenerator),
60 changes: 60 additions & 0 deletions pkg/resources/procedure_acceptance_test.go
Original file line number Diff line number Diff line change
@@ -508,3 +508,63 @@ resource "snowflake_procedure" "p" {
}
`, database, schema, name)
}

func TestAcc_Procedure_EnsureSmoothResourceIdMigrationToV0950_ArgumentSynonyms(t *testing.T) {
name := acc.TestClient().Ids.RandomAccountObjectIdentifier().Name()
resourceName := "snowflake_procedure.p"

resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_5_0),
},
CheckDestroy: acc.CheckDestroy(t, resources.Procedure),
Steps: []resource.TestStep{
{
ExternalProviders: map[string]resource.ExternalProvider{
"snowflake": {
VersionConstraint: "=0.94.1",
Source: "Snowflake-Labs/snowflake",
},
},
Config: procedureConfigWithArgumentSynonyms(acc.TestDatabaseName, acc.TestSchemaName, name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf(`"%s"."%s"."%s"(NUMBER, VARCHAR)`, acc.TestDatabaseName, acc.TestSchemaName, name)),
),
},
{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
Config: procedureConfigWithArgumentSynonyms(acc.TestDatabaseName, acc.TestSchemaName, name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf(`"%s"."%s"."%s"(NUMBER, VARCHAR)`, acc.TestDatabaseName, acc.TestSchemaName, name)),
),
},
},
})
}

func procedureConfigWithArgumentSynonyms(database string, schema string, name string) string {
return fmt.Sprintf(`
resource "snowflake_procedure" "p" {
database = "%[1]s"
schema = "%[2]s"
name = "%[3]s"
return_type = "VARCHAR"
return_behavior = "IMMUTABLE"
statement = <<EOT
BEGIN
RETURN B;
END;
EOT
arguments {
name = "A"
type = "INT"
}
arguments {
name = "B"
type = "TEXT"
}
}
`, database, schema, name)
}
4 changes: 2 additions & 2 deletions pkg/sdk/external_functions_impl_gen.go
Original file line number Diff line number Diff line change
@@ -171,10 +171,10 @@ func (r externalFunctionRow) convert() *ExternalFunction {
e.Arguments = dataTypes
}
if r.SchemaName.Valid {
e.SchemaName = r.SchemaName.String
e.SchemaName = strings.Trim(r.SchemaName.String, `"`)
}
if r.CatalogName.Valid {
e.CatalogName = r.CatalogName.String
e.CatalogName = strings.Trim(r.CatalogName.String, `"`)
}
if r.IsSecure.Valid {
e.IsSecure = r.IsSecure.String == "Y"
12 changes: 11 additions & 1 deletion pkg/sdk/identifier_helpers.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package sdk
import (
"encoding/csv"
"fmt"
"log"
"strings"
)

@@ -342,11 +343,20 @@ type SchemaObjectIdentifierWithArguments struct {
}

func NewSchemaObjectIdentifierWithArguments(databaseName, schemaName, name string, argumentDataTypes ...DataType) SchemaObjectIdentifierWithArguments {
// Arguments have to be "normalized" with ToDataType, so the signature would match with the one returned by Snowflake.
normalizedArguments := make([]DataType, len(argumentDataTypes))
for i, argument := range argumentDataTypes {
normalizedArgument, err := ToDataType(string(argument))
if err != nil {
log.Printf("[DEBUG] failed to normalize argument %d: %v, err = %v", i, argument, err)
}
normalizedArguments[i] = normalizedArgument
}
return SchemaObjectIdentifierWithArguments{
databaseName: strings.Trim(databaseName, `"`),
schemaName: strings.Trim(schemaName, `"`),
name: strings.Trim(name, `"`),
argumentDataTypes: argumentDataTypes,
argumentDataTypes: normalizedArguments,
}
}

23 changes: 23 additions & 0 deletions pkg/sdk/testint/external_functions_integration_test.go
Original file line number Diff line number Diff line change
@@ -246,6 +246,29 @@ func TestInt_ExternalFunctions(t *testing.T) {
require.Equal(t, *e, *es)
})

t.Run("show external function by id - different name, same arguments", func(t *testing.T) {
id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
e := testClientHelper().ExternalFunction.CreateWithIdentifier(t, integration.ID(), id1)
testClientHelper().ExternalFunction.CreateWithIdentifier(t, integration.ID(), id2)

es, err := client.ExternalFunctions.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})

t.Run("show external function by id - same name, different arguments", func(t *testing.T) {
name := testClientHelper().Ids.Alpha()
id1 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeVARCHAR)
e := testClientHelper().ExternalFunction.CreateWithIdentifier(t, integration.ID(), id1)
testClientHelper().ExternalFunction.CreateWithIdentifier(t, integration.ID(), id2)

es, err := client.ExternalFunctions.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})

t.Run("describe external function", func(t *testing.T) {
e := createExternalFunction(t)

23 changes: 23 additions & 0 deletions pkg/sdk/testint/functions_integration_test.go
Original file line number Diff line number Diff line change
@@ -499,6 +499,29 @@ func TestInt_FunctionsShowByID(t *testing.T) {
require.Equal(t, id2, e2Id)
})

t.Run("show function by id - different name, same arguments", func(t *testing.T) {
id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
e := testClientHelper().Function.CreateWithIdentifier(t, id1)
testClientHelper().Function.CreateWithIdentifier(t, id2)

es, err := client.Functions.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})

t.Run("show function by id - same name, different arguments", func(t *testing.T) {
name := testClientHelper().Ids.Alpha()
id1 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeVARCHAR)
e := testClientHelper().Function.CreateWithIdentifier(t, id1)
testClientHelper().Function.CreateWithIdentifier(t, id2)

es, err := client.Functions.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})

t.Run("function returns non detailed data types of arguments", func(t *testing.T) {
// This test proves that every detailed data types (e.g. VARCHAR(20) and NUMBER(10, 0)) are generalized
// on Snowflake side (to e.g. VARCHAR and NUMBER) and that sdk.ToDataType mapping function maps detailed types
27 changes: 25 additions & 2 deletions pkg/sdk/testint/procedures_integration_test.go
Original file line number Diff line number Diff line change
@@ -216,7 +216,7 @@ func TestInt_CreateProcedures(t *testing.T) {
t.Run("create procedure for Python: returns result data type", func(t *testing.T) {
// https://docs.snowflake.com/en/developer-guide/stored-procedure/stored-procedures-python#running-concurrent-tasks-with-worker-processes
name := "joblib_multiprocessing_proc"
id := testClientHelper().Ids.NewSchemaObjectIdentifierWithArguments(name, "INT")
id := testClientHelper().Ids.NewSchemaObjectIdentifierWithArguments(name, sdk.DataTypeInt)

definition := `
import joblib
@@ -227,7 +227,7 @@ def joblib_multiprocessing(session, i):

dt := sdk.NewProcedureReturnsResultDataTypeRequest(sdk.DataTypeString)
returns := sdk.NewProcedureReturnsRequest().WithResultDataType(*dt)
argument := sdk.NewProcedureArgumentRequest("i", "INT")
argument := sdk.NewProcedureArgumentRequest("i", sdk.DataTypeInt)
packages := []sdk.ProcedurePackageRequest{
*sdk.NewProcedurePackageRequest("snowflake-snowpark-python"),
*sdk.NewProcedurePackageRequest("joblib"),
@@ -1021,4 +1021,27 @@ func TestInt_ProceduresShowByID(t *testing.T) {
require.NoError(t, err)
require.Equal(t, id2, e2.ID())
})

t.Run("show procedure by id - different name, same arguments", func(t *testing.T) {
id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
e := testClientHelper().Procedure.CreateWithIdentifier(t, id1)
testClientHelper().Procedure.CreateWithIdentifier(t, id2)

es, err := client.Procedures.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})

t.Run("show procedure by id - same name, different arguments", func(t *testing.T) {
name := testClientHelper().Ids.Alpha()
id1 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR)
id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeVARCHAR)
e := testClientHelper().Procedure.CreateWithIdentifier(t, id1)
testClientHelper().Procedure.CreateWithIdentifier(t, id2)

es, err := client.Procedures.ShowByID(ctx, id1)
require.NoError(t, err)
require.Equal(t, *e, *es)
})
}