diff --git a/pkg/resources/function_and_procedure_commons.go b/pkg/resources/function_and_procedure_commons.go index 213217e968..daf0709af7 100644 --- a/pkg/resources/function_and_procedure_commons.go +++ b/pkg/resources/function_and_procedure_commons.go @@ -26,6 +26,17 @@ func readFunctionOrProcedureArguments(d *schema.ResourceData, args []sdk.Normali } } +func importFunctionOrProcedureArguments(d *schema.ResourceData, args []sdk.NormalizedArgument) error { + currentArgs := make([]map[string]any, len(args)) + for i, arg := range args { + currentArgs[i] = map[string]any{ + "arg_name": arg.Name, + "arg_data_type": arg.DataType.ToSql(), + } + } + return d.Set("arguments", currentArgs) +} + func readFunctionOrProcedureImports(d *schema.ResourceData, imports []sdk.NormalizedPath) error { if len(imports) == 0 { // don't do anything if imports not present diff --git a/pkg/resources/function_commons.go b/pkg/resources/function_commons.go index 12ea55bd73..c9c8bf05e6 100644 --- a/pkg/resources/function_commons.go +++ b/pkg/resources/function_commons.go @@ -9,6 +9,7 @@ import ( "slices" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -511,6 +512,41 @@ func UpdateFunction(language string, readFunc func(ctx context.Context, d *schem } } +func ImportFunction(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting function import") + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifierWithArguments(d.Id()) + if err != nil { + return nil, err + } + + functionDetails, err := client.Functions.DescribeDetails(ctx, id) + if err != nil { + return nil, err + } + + function, err := client.Functions.ShowByID(ctx, id) + if err != nil { + return nil, err + } + + err = errors.Join( + d.Set("database", id.DatabaseName()), + d.Set("schema", id.SchemaName()), + d.Set("name", id.Name()), + d.Set("is_secure", booleanStringFromBool(function.IsSecure)), + setOptionalFromStringPtr(d, "null_input_behavior", functionDetails.NullHandling), + setOptionalFromStringPtr(d, "return_results_behavior", functionDetails.Volatility), + importFunctionOrProcedureArguments(d, functionDetails.NormalizedArguments), + // all others are set in read + ) + if err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + // TODO [SNOW-1850370]: Make the rest of the functions in this file generic (for reuse with procedures) func parseFunctionArgumentsCommon(d *schema.ResourceData) ([]sdk.FunctionArgumentRequest, error) { args := make([]sdk.FunctionArgumentRequest, 0) diff --git a/pkg/resources/function_java.go b/pkg/resources/function_java.go index 4dae73f94c..14053affed 100644 --- a/pkg/resources/function_java.go +++ b/pkg/resources/function_java.go @@ -40,7 +40,7 @@ func FunctionJava() *schema.Resource { Schema: collections.MergeMaps(javaFunctionSchema, functionParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.FunctionJava, ImportFunction), }, } } diff --git a/pkg/resources/function_java_acceptance_test.go b/pkg/resources/function_java_acceptance_test.go index 9b8f032779..8e30bf28e4 100644 --- a/pkg/resources/function_java_acceptance_test.go +++ b/pkg/resources/function_java_acceptance_test.go @@ -17,6 +17,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -27,7 +28,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -// TODO [SNOW-1348103]: test import // TODO [SNOW-1348103]: test external changes // TODO [SNOW-1348103]: test changes of attributes separately @@ -77,6 +77,20 @@ func TestAcc_FunctionJava_InlineBasic(t *testing.T) { assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), ), }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"is_secure", "arguments.0.arg_data_type", "null_input_behavior", "return_results_behavior"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionJavaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, // RENAME { Config: config.FromModels(t, functionModelRenamed), @@ -220,6 +234,9 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). WithRuntimeVersion("11"). + WithIsSecure("false"). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). WithComment("some comment") functionModelUpdateWithoutRecreation := model.FunctionJavaBasicInline("w", id, dataType, handler, definition). @@ -235,6 +252,9 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). WithRuntimeVersion("11"). + WithIsSecure(r.BooleanFalse). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). WithComment("some other comment") resource.Test(t, resource.TestCase{ @@ -251,7 +271,7 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { Check: assert.AssertThat(t, resourceassert.FunctionJavaResource(t, functionModel.ResourceReference()). HasNameString(id.Name()). - HasIsSecureString(r.BooleanDefault). + HasIsSecureString(r.BooleanFalse). HasImportsLength(2). HasRuntimeVersionString("11"). HasFunctionDefinitionString(definition). @@ -267,6 +287,20 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { HasIsSecure(false), ), }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionJavaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, // UPDATE WITHOUT RECREATION { ConfigPlanChecks: resource.ConfigPlanChecks{ @@ -278,7 +312,7 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { Check: assert.AssertThat(t, resourceassert.FunctionJavaResource(t, functionModelUpdateWithoutRecreation.ResourceReference()). HasNameString(id.Name()). - HasIsSecureString(r.BooleanDefault). + HasIsSecureString(r.BooleanFalse). HasImportsLength(2). HasRuntimeVersionString("11"). HasFunctionDefinitionString(definition). diff --git a/pkg/resources/function_javascript.go b/pkg/resources/function_javascript.go index fe0884dd67..ff98838485 100644 --- a/pkg/resources/function_javascript.go +++ b/pkg/resources/function_javascript.go @@ -40,7 +40,7 @@ func FunctionJavascript() *schema.Resource { Schema: collections.MergeMaps(javascriptFunctionSchema, functionParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.FunctionJavascript, ImportFunction), }, } } diff --git a/pkg/resources/function_python.go b/pkg/resources/function_python.go index ebc3dd7259..1a6f9e6788 100644 --- a/pkg/resources/function_python.go +++ b/pkg/resources/function_python.go @@ -40,7 +40,7 @@ func FunctionPython() *schema.Resource { Schema: collections.MergeMaps(pythonFunctionSchema, functionParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.FunctionPython, ImportFunction), }, } } diff --git a/pkg/resources/function_scala.go b/pkg/resources/function_scala.go index 491af794b4..c323a54a27 100644 --- a/pkg/resources/function_scala.go +++ b/pkg/resources/function_scala.go @@ -40,7 +40,7 @@ func FunctionScala() *schema.Resource { Schema: collections.MergeMaps(scalaFunctionSchema, functionParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.FunctionScala, ImportFunction), }, } } diff --git a/pkg/resources/function_sql.go b/pkg/resources/function_sql.go index 53694da3a1..4a30186e53 100644 --- a/pkg/resources/function_sql.go +++ b/pkg/resources/function_sql.go @@ -40,7 +40,7 @@ func FunctionSql() *schema.Resource { Schema: collections.MergeMaps(sqlFunctionSchema, functionParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.FunctionSql, ImportFunction), }, } } @@ -114,7 +114,6 @@ func ReadContextFunctionSql(ctx context.Context, d *schema.ResourceData, meta an // not reading is_secure on purpose (handled as external change to show output) readFunctionOrProcedureArguments(d, allFunctionDetails.functionDetails.NormalizedArguments), d.Set("return_type", allFunctionDetails.functionDetails.ReturnDataType.ToSql()), - // not reading null_input_behavior on purpose (handled as external change to show output) // not reading return_results_behavior on purpose (handled as external change to show output) d.Set("comment", allFunctionDetails.function.Description), setRequiredFromStringPtr(d, "handler", allFunctionDetails.functionDetails.Handler), diff --git a/pkg/resources/procedure_commons.go b/pkg/resources/procedure_commons.go index 12d3645388..83d79963a5 100644 --- a/pkg/resources/procedure_commons.go +++ b/pkg/resources/procedure_commons.go @@ -9,6 +9,7 @@ import ( "slices" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -480,6 +481,40 @@ func UpdateProcedure(language string, readFunc func(ctx context.Context, d *sche } } +func ImportProcedure(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting procedure import") + client := meta.(*provider.Context).Client + id, err := sdk.ParseSchemaObjectIdentifierWithArguments(d.Id()) + if err != nil { + return nil, err + } + + procedureDetails, err := client.Procedures.DescribeDetails(ctx, id) + if err != nil { + return nil, err + } + + procedure, err := client.Procedures.ShowByID(ctx, id) + if err != nil { + return nil, err + } + + err = errors.Join( + d.Set("database", id.DatabaseName()), + d.Set("schema", id.SchemaName()), + d.Set("name", id.Name()), + d.Set("is_secure", booleanStringFromBool(procedure.IsSecure)), + setOptionalFromStringPtr(d, "null_input_behavior", procedureDetails.NullHandling), + importFunctionOrProcedureArguments(d, procedureDetails.NormalizedArguments), + // all others are set in read + ) + if err != nil { + return nil, err + } + + return []*schema.ResourceData{d}, nil +} + func queryAllProcedureDetailsCommon(ctx context.Context, d *schema.ResourceData, client *sdk.Client, id sdk.SchemaObjectIdentifierWithArguments) (*allProcedureDetailsCommon, diag.Diagnostics) { procedureDetails, err := client.Procedures.DescribeDetails(ctx, id) if err != nil { diff --git a/pkg/resources/procedure_java.go b/pkg/resources/procedure_java.go index 1d98f7cf2a..584c4da787 100644 --- a/pkg/resources/procedure_java.go +++ b/pkg/resources/procedure_java.go @@ -41,7 +41,7 @@ func ProcedureJava() *schema.Resource { Schema: collections.MergeMaps(javaProcedureSchema, procedureParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.ProcedureJava, ImportProcedure), }, } } diff --git a/pkg/resources/procedure_java_acceptance_test.go b/pkg/resources/procedure_java_acceptance_test.go index c6d0aba743..b7a81a0b40 100644 --- a/pkg/resources/procedure_java_acceptance_test.go +++ b/pkg/resources/procedure_java_acceptance_test.go @@ -16,6 +16,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -26,7 +27,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -// TODO [SNOW-1348103]: test import // TODO [SNOW-1348103]: test external changes // TODO [SNOW-1348103]: test changes of attributes separately @@ -76,6 +76,20 @@ func TestAcc_ProcedureJava_InlineBasic(t *testing.T) { assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "arguments.0.arg_default_value", "")), ), }, + // IMPORT + { + ResourceName: procedureModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"is_secure", "arguments.0.arg_data_type", "null_input_behavior"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedProcedureJavaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, // RENAME { Config: config.FromModels(t, procedureModelRenamed), @@ -220,6 +234,8 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). WithRuntimeVersion("11"). + WithIsSecure("false"). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). WithComment("some comment") procedureModelUpdateWithoutRecreation := model.ProcedureJavaBasicInline("w", id, dataType, handler, definition). @@ -236,6 +252,8 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). WithRuntimeVersion("11"). + WithIsSecure("false"). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). WithComment("some other comment") resource.Test(t, resource.TestCase{ @@ -252,7 +270,7 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { Check: assert.AssertThat(t, resourceassert.ProcedureJavaResource(t, procedureModel.ResourceReference()). HasNameString(id.Name()). - HasIsSecureString(r.BooleanDefault). + HasIsSecureString(r.BooleanFalse). HasImportsLength(2). HasRuntimeVersionString("11"). HasProcedureDefinitionString(definition). @@ -269,6 +287,20 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { HasIsSecure(false), ), }, + // IMPORT + { + ResourceName: procedureModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedProcedureJavaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, // UPDATE WITHOUT RECREATION { ConfigPlanChecks: resource.ConfigPlanChecks{ @@ -280,7 +312,7 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { Check: assert.AssertThat(t, resourceassert.ProcedureJavaResource(t, procedureModelUpdateWithoutRecreation.ResourceReference()). HasNameString(id.Name()). - HasIsSecureString(r.BooleanDefault). + HasIsSecureString(r.BooleanFalse). HasImportsLength(2). HasRuntimeVersionString("11"). HasProcedureDefinitionString(definition). diff --git a/pkg/resources/procedure_javascript.go b/pkg/resources/procedure_javascript.go index 4a273e28f5..97336f2839 100644 --- a/pkg/resources/procedure_javascript.go +++ b/pkg/resources/procedure_javascript.go @@ -40,7 +40,7 @@ func ProcedureJavascript() *schema.Resource { Schema: collections.MergeMaps(javascriptProcedureSchema, procedureParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.ProcedureJavascript, ImportProcedure), }, } } diff --git a/pkg/resources/procedure_python.go b/pkg/resources/procedure_python.go index 0432fbb966..deb95c9871 100644 --- a/pkg/resources/procedure_python.go +++ b/pkg/resources/procedure_python.go @@ -41,7 +41,7 @@ func ProcedurePython() *schema.Resource { Schema: collections.MergeMaps(pythonProcedureSchema, procedureParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.ProcedurePython, ImportProcedure), }, } } diff --git a/pkg/resources/procedure_scala.go b/pkg/resources/procedure_scala.go index 0a5dc691d0..c860c6282d 100644 --- a/pkg/resources/procedure_scala.go +++ b/pkg/resources/procedure_scala.go @@ -41,7 +41,7 @@ func ProcedureScala() *schema.Resource { Schema: collections.MergeMaps(scalaProcedureSchema, procedureParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.ProcedureScala, ImportProcedure), }, } } diff --git a/pkg/resources/procedure_sql.go b/pkg/resources/procedure_sql.go index 64ddfde270..d38717904a 100644 --- a/pkg/resources/procedure_sql.go +++ b/pkg/resources/procedure_sql.go @@ -40,7 +40,7 @@ func ProcedureSql() *schema.Resource { Schema: collections.MergeMaps(sqlProcedureSchema, procedureParametersSchema), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: TrackingImportWrapper(resources.ProcedureSql, ImportProcedure), }, } }