From 14549677fef18a87a2cb360ac77caa5ecc4adaab Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 01:13:44 +0100 Subject: [PATCH 01/10] handle missing fields in function and procedure starts here --- pkg/sdk/functions_and_procedures_commons.go | 7 +++++++ pkg/sdk/functions_ext.go | 19 ++++++++++++++----- pkg/sdk/procedures_ext.go | 19 ++++++++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/pkg/sdk/functions_and_procedures_commons.go b/pkg/sdk/functions_and_procedures_commons.go index df64aba187..713b5f086c 100644 --- a/pkg/sdk/functions_and_procedures_commons.go +++ b/pkg/sdk/functions_and_procedures_commons.go @@ -5,6 +5,7 @@ import ( "log" "strings" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" ) @@ -116,3 +117,9 @@ func parseFunctionOrProcedureArgument(arg string) (*NormalizedArgument, error) { } return &NormalizedArgument{argName, dt}, nil } + +// TODO [SNOW-1850370]: is this combo enough? - e.g. whitespace looks to be not trimmed +func parseFunctionOrProcedureExternalAccessIntegrations(raw string) ([]AccountObjectIdentifier, error) { + log.Printf("[DEBUG] external access integrations: %s", raw) + return collections.MapErr(ParseCommaSeparatedStringArray(raw, false), ParseAccountObjectIdentifier) +} diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index facd9ede1d..1d4f0ef0b4 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -33,11 +33,12 @@ type FunctionDetails struct { InstalledPackages *string // list present for python (hidden when SECURE) IsAggregate *bool // present for python - NormalizedImports []NormalizedPath - NormalizedTargetPath *NormalizedPath - ReturnDataType datatypes.DataType - ReturnNotNull bool - NormalizedArguments []NormalizedArgument + NormalizedImports []NormalizedPath + NormalizedTargetPath *NormalizedPath + ReturnDataType datatypes.DataType + ReturnNotNull bool + NormalizedArguments []NormalizedArgument + NormalizedExternalAccessIntegrations []AccountObjectIdentifier } func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { @@ -108,6 +109,14 @@ func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v.NormalizedArguments = args } + if v.ExternalAccessIntegrations != nil { + if p, err := parseFunctionOrProcedureExternalAccessIntegrations(*v.ExternalAccessIntegrations); err != nil { + errs = append(errs, err) + } else { + v.NormalizedExternalAccessIntegrations = p + } + } + return v, errors.Join(errs...) } diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index de40fd8732..76de414bae 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -33,11 +33,12 @@ type ProcedureDetails struct { InstalledPackages *string // list present for python (hidden when SECURE) ExecuteAs string // present for all procedure types - NormalizedImports []NormalizedPath - NormalizedTargetPath *NormalizedPath - ReturnDataType datatypes.DataType - ReturnNotNull bool - NormalizedArguments []NormalizedArgument + NormalizedImports []NormalizedPath + NormalizedTargetPath *NormalizedPath + ReturnDataType datatypes.DataType + ReturnNotNull bool + NormalizedArguments []NormalizedArgument + NormalizedExternalAccessIntegrations []AccountObjectIdentifier } func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) { @@ -108,6 +109,14 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) v.NormalizedArguments = args } + if v.ExternalAccessIntegrations != nil { + if p, err := parseFunctionOrProcedureExternalAccessIntegrations(*v.ExternalAccessIntegrations); err != nil { + errs = append(errs, err) + } else { + v.NormalizedExternalAccessIntegrations = p + } + } + return v, errors.Join(errs...) } From 79fdca960e89e3f2ee17353f38f6a3cf8fb8cdd4 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 01:14:09 +0100 Subject: [PATCH 02/10] Normalize and test external access integrations --- .../function_describe_snowflake_ext.go | 18 ++++++++++++++++- .../procedure_describe_snowflake_ext.go | 16 +++++++++++++++ pkg/sdk/functions_ext.go | 2 ++ pkg/sdk/procedures_ext.go | 2 ++ pkg/sdk/testint/functions_integration_test.go | 19 ++++++++++++++++++ .../testint/procedures_integration_test.go | 20 +++++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go index a4c256b172..d5f09abe3f 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -415,7 +415,7 @@ func (f *FunctionDetailsAssert) HasExactlyImportsNormalizedInAnyOrder(imports .. return fmt.Errorf("expected imports to have value; got: nil") } if !assert2.ElementsMatch(t, imports, o.NormalizedImports) { - return fmt.Errorf("expected %v imports in task relations, got %v", imports, o.NormalizedImports) + return fmt.Errorf("expected %v imports, got %v", imports, o.NormalizedImports) } return nil }) @@ -474,3 +474,19 @@ func (f *FunctionDetailsAssert) HasReturnNotNull(expected bool) *FunctionDetails }) return f } + +func (f *FunctionDetailsAssert) HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(integrations ...sdk.AccountObjectIdentifier) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.NormalizedExternalAccessIntegrations == nil { + return fmt.Errorf("expected normalized external access integrations to have value; got: nil") + } + fullyQualifiedNamesExpected := collections.Map(integrations, func(id sdk.AccountObjectIdentifier) string { return id.FullyQualifiedName() }) + fullyQualifiedNamesGot := collections.Map(o.NormalizedExternalAccessIntegrations, func(id sdk.AccountObjectIdentifier) string { return id.FullyQualifiedName() }) + if !assert2.ElementsMatch(t, fullyQualifiedNamesExpected, fullyQualifiedNamesGot) { + return fmt.Errorf("expected %v normalized external access integrations, got %v", integrations, o.NormalizedExternalAccessIntegrations) + } + return nil + }) + return f +} diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go index 2319b30f7a..550c4c5c1f 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go @@ -460,3 +460,19 @@ func (f *ProcedureDetailsAssert) HasReturnNotNull(expected bool) *ProcedureDetai }) return f } + +func (f *ProcedureDetailsAssert) HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(integrations ...sdk.AccountObjectIdentifier) *ProcedureDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.ProcedureDetails) error { + t.Helper() + if o.NormalizedExternalAccessIntegrations == nil { + return fmt.Errorf("expected normalized external access integrations to have value; got: nil") + } + fullyQualifiedNamesExpected := collections.Map(integrations, func(id sdk.AccountObjectIdentifier) string { return id.FullyQualifiedName() }) + fullyQualifiedNamesGot := collections.Map(o.NormalizedExternalAccessIntegrations, func(id sdk.AccountObjectIdentifier) string { return id.FullyQualifiedName() }) + if !assert2.ElementsMatch(t, fullyQualifiedNamesExpected, fullyQualifiedNamesGot) { + return fmt.Errorf("expected %v normalized external access integrations, got %v", integrations, o.NormalizedExternalAccessIntegrations) + } + return nil + }) + return f +} diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 1d4f0ef0b4..0b9d0f277b 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -115,6 +115,8 @@ func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { } else { v.NormalizedExternalAccessIntegrations = p } + } else { + v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } return v, errors.Join(errs...) diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index 76de414bae..91e50e6cba 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -115,6 +115,8 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) } else { v.NormalizedExternalAccessIntegrations = p } + } else { + v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } return v, errors.Join(errs...) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 022ba7592a..4428e9ca6f 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -119,6 +119,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -295,6 +296,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, importPath)). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -383,6 +385,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -529,6 +532,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -606,6 +610,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -679,6 +684,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -765,6 +771,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -838,6 +845,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -923,6 +931,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -999,6 +1008,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -1094,6 +1104,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1168,6 +1179,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, importPath)). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1253,6 +1265,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1326,6 +1339,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandlingNil(). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1434,6 +1448,7 @@ func TestInt_Functions(t *testing.T) { // HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1502,6 +1517,7 @@ func TestInt_Functions(t *testing.T) { HasNullHandlingNil(). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1581,6 +1597,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(), ) @@ -1609,6 +1626,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), ) @@ -1636,6 +1654,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). // TODO [SNOW-1850370]: apparently UNSET external access integrations cleans out secrets in the describe but leaves it in SHOW HasSecretsNil(), ) diff --git a/pkg/sdk/testint/procedures_integration_test.go b/pkg/sdk/testint/procedures_integration_test.go index c5434d6308..b737d55b22 100644 --- a/pkg/sdk/testint/procedures_integration_test.go +++ b/pkg/sdk/testint/procedures_integration_test.go @@ -107,6 +107,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -197,6 +198,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -273,6 +275,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, importPath)). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -356,6 +359,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -500,6 +504,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -572,6 +577,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -644,6 +650,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -728,6 +735,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -800,6 +808,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -883,6 +892,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -959,6 +969,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(`[]`). HasExactlyImportsNormalizedInAnyOrder(). @@ -1050,6 +1061,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1123,6 +1135,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImports(fmt.Sprintf(`[%s]`, importPath)). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1208,6 +1221,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ @@ -1277,6 +1291,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandlingNil(). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1382,6 +1397,7 @@ func TestInt_Procedures(t *testing.T) { HasVolatilityNil(). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1446,6 +1462,7 @@ func TestInt_Procedures(t *testing.T) { HasNullHandlingNil(). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(). HasImportsNil(). HasExactlyImportsNormalizedInAnyOrder(). @@ -1828,6 +1845,7 @@ def filter_by_role(session, table_name, role): assertions.AssertThatObject(t, objectassert.ProcedureDetails(t, id). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). HasSecretsNil(), ) @@ -1858,6 +1876,7 @@ def filter_by_role(session, table_name, role): assertions.AssertThatObject(t, objectassert.ProcedureDetails(t, id). HasExactlyExternalAccessIntegrations(externalAccessIntegration). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), ) @@ -1886,6 +1905,7 @@ def filter_by_role(session, table_name, role): assertions.AssertThatObject(t, objectassert.ProcedureDetails(t, id). HasExternalAccessIntegrationsNil(). + HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(). // TODO [SNOW-1850370]: apparently UNSET external access integrations cleans out secrets in the describe but leaves it in SHOW HasSecretsNil(), ) From 56d59e5a342402db755ef2d19c2905f7ee3d0480 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 07:56:23 +0100 Subject: [PATCH 03/10] Normalize packages --- pkg/sdk/functions_and_procedures_commons.go | 12 ++++ pkg/sdk/functions_ext.go | 12 ++++ pkg/sdk/procedures_ext.go | 55 ++++++++++++++++++- pkg/sdk/testint/functions_integration_test.go | 1 + 4 files changed, 79 insertions(+), 1 deletion(-) diff --git a/pkg/sdk/functions_and_procedures_commons.go b/pkg/sdk/functions_and_procedures_commons.go index 713b5f086c..75c80d843c 100644 --- a/pkg/sdk/functions_and_procedures_commons.go +++ b/pkg/sdk/functions_and_procedures_commons.go @@ -123,3 +123,15 @@ func parseFunctionOrProcedureExternalAccessIntegrations(raw string) ([]AccountOb log.Printf("[DEBUG] external access integrations: %s", raw) return collections.MapErr(ParseCommaSeparatedStringArray(raw, false), ParseAccountObjectIdentifier) } + +// TODO [test before V1]: +func parseFunctionOrProcedurePackages(raw string) ([]string, error) { + log.Printf("[DEBUG] external access integrations: %s", raw) + return collections.Map(ParseCommaSeparatedStringArray(raw, false), strings.TrimSpace), nil +} + +// TODO [this PR]: parse them +func parseFunctionOrProcedureSecrets(raw string) (map[string]SchemaObjectIdentifier, error) { + log.Printf("[DEBUG] parsing secrets: %s", raw) + return nil, nil +} diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 0b9d0f277b..b0d0429f3f 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -39,6 +39,8 @@ type FunctionDetails struct { ReturnNotNull bool NormalizedArguments []NormalizedArgument NormalizedExternalAccessIntegrations []AccountObjectIdentifier + NormalizedSecrets map[string]SchemaObjectIdentifier + NormalizedPackages []string } func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { @@ -119,6 +121,16 @@ func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } + if v.Packages != nil { + if p, err := parseFunctionOrProcedurePackages(*v.Packages); err != nil { + errs = append(errs, err) + } else { + v.NormalizedPackages = p + } + } else { + v.NormalizedPackages = []string{} + } + return v, errors.Join(errs...) } diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index 91e50e6cba..71409a86ed 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -5,11 +5,16 @@ import ( "errors" "fmt" "strconv" + "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" ) -const DefaultProcedureComment = "user-defined procedure" +const ( + DefaultProcedureComment = "user-defined procedure" + JavaSnowparkPackageString = "com.snowflake:snowpark:" + PythonSnowparkPackageString = "snowflake-snowpark-python==" +) func (v *Procedure) ID() SchemaObjectIdentifierWithArguments { return NewSchemaObjectIdentifierWithArguments(v.CatalogName, v.SchemaName, v.Name, v.ArgumentsOld...) @@ -39,6 +44,10 @@ type ProcedureDetails struct { ReturnNotNull bool NormalizedArguments []NormalizedArgument NormalizedExternalAccessIntegrations []AccountObjectIdentifier + NormalizedSecrets map[string]SchemaObjectIdentifier + // NormalizedPackages does not contain a snowpark package - it is extracted only as a version in SnowparkVersion below + NormalizedPackages []string + SnowparkVersion string } func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) { @@ -119,6 +128,50 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } + if v.Packages != nil { + if p, err := parseFunctionOrProcedurePackages(*v.Packages); err != nil { + errs = append(errs, err) + } else { + // TODO [SNOW-1850370]: merge these and unit test + switch strings.ToUpper(v.Language) { + case "JAVA", "SCALA": + filtered := make([]string, 0) + var found bool + for _, o := range p { + o := strings.TrimSpace(o) + if strings.HasPrefix(o, JavaSnowparkPackageString) { + v.SnowparkVersion = strings.TrimPrefix(o, JavaSnowparkPackageString) + found = true + } else { + filtered = append(filtered, o) + } + } + v.NormalizedPackages = filtered + if !found { + errs = append(errs, fmt.Errorf("could not parse package from Snowflake, expected at least snowpark package, got %v", filtered)) + } + case "PYTHON": + filtered := make([]string, 0) + var found bool + for _, o := range p { + o := strings.TrimSpace(o) + if strings.HasPrefix(o, PythonSnowparkPackageString) { + v.SnowparkVersion = strings.TrimPrefix(o, PythonSnowparkPackageString) + found = true + } else { + filtered = append(filtered, o) + } + } + v.NormalizedPackages = filtered + if !found { + errs = append(errs, fmt.Errorf("could not parse package from Snowflake, expected at least snowpark package, got %v", filtered)) + } + } + } + } else { + errs = append(errs, fmt.Errorf("could not parse package from Snowflake, expected at least snowpark package, got nil")) + } + return v, errors.Join(errs...) } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 4428e9ca6f..b14aa44441 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -34,6 +34,7 @@ import ( // TODO [SNOW-1348103]: test secure // TODO [SNOW-1348103]: python aggregate func (100357 (P0000): Could not find accumulate method in function CVVEMHIT_06547800_08D6_DBCA_1AC7_5E422AFF8B39 with handler dump) // TODO [SNOW-1348103]: add test with multiple imports +// TODO [this PR]: test with multiple external access integrations and secrets func TestInt_Functions(t *testing.T) { client := testClient(t) ctx := context.Background() From 6db5c5ef1db2aedc6ed2584fa141e642537d7fec Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 08:01:04 +0100 Subject: [PATCH 04/10] Normalize secrets --- pkg/sdk/functions_and_procedures_commons.go | 20 +++++++++++++++++--- pkg/sdk/functions_ext.go | 10 ++++++++++ pkg/sdk/procedures_ext.go | 10 ++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/pkg/sdk/functions_and_procedures_commons.go b/pkg/sdk/functions_and_procedures_commons.go index 75c80d843c..7da4f1e57d 100644 --- a/pkg/sdk/functions_and_procedures_commons.go +++ b/pkg/sdk/functions_and_procedures_commons.go @@ -1,6 +1,7 @@ package sdk import ( + "encoding/json" "fmt" "log" "strings" @@ -124,14 +125,27 @@ func parseFunctionOrProcedureExternalAccessIntegrations(raw string) ([]AccountOb return collections.MapErr(ParseCommaSeparatedStringArray(raw, false), ParseAccountObjectIdentifier) } -// TODO [test before V1]: +// TODO [before V1]: test func parseFunctionOrProcedurePackages(raw string) ([]string, error) { log.Printf("[DEBUG] external access integrations: %s", raw) return collections.Map(ParseCommaSeparatedStringArray(raw, false), strings.TrimSpace), nil } -// TODO [this PR]: parse them +// TODO [before V1]: unit test func parseFunctionOrProcedureSecrets(raw string) (map[string]SchemaObjectIdentifier, error) { log.Printf("[DEBUG] parsing secrets: %s", raw) - return nil, nil + secrets := make(map[string]string) + err := json.Unmarshal([]byte(raw), &secrets) + if err != nil { + return nil, fmt.Errorf("could not parse secrets from Snowflake: %s, err: %w", raw, err) + } + normalizedSecrets := make(map[string]SchemaObjectIdentifier) + for k, v := range secrets { + id, err := ParseSchemaObjectIdentifier(v) + if err != nil { + return nil, fmt.Errorf("could not parse secrets from Snowflake: %s, err: %w", raw, err) + } + normalizedSecrets[k] = id + } + return normalizedSecrets, nil } diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index b0d0429f3f..93ae4ffa8e 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -121,6 +121,16 @@ func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } + if v.Secrets != nil { + if p, err := parseFunctionOrProcedureSecrets(*v.Secrets); err != nil { + errs = append(errs, err) + } else { + v.NormalizedSecrets = p + } + } else { + v.NormalizedSecrets = map[string]SchemaObjectIdentifier{} + } + if v.Packages != nil { if p, err := parseFunctionOrProcedurePackages(*v.Packages); err != nil { errs = append(errs, err) diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index 71409a86ed..95ad886a8a 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -128,6 +128,16 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) v.NormalizedExternalAccessIntegrations = []AccountObjectIdentifier{} } + if v.Secrets != nil { + if p, err := parseFunctionOrProcedureSecrets(*v.Secrets); err != nil { + errs = append(errs, err) + } else { + v.NormalizedSecrets = p + } + } else { + v.NormalizedSecrets = map[string]SchemaObjectIdentifier{} + } + if v.Packages != nil { if p, err := parseFunctionOrProcedurePackages(*v.Packages); err != nil { errs = append(errs, err) From b86f2a67e3902479441d63cb888c373469cef147 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 08:23:29 +0100 Subject: [PATCH 05/10] Test secrets and packages --- .../function_describe_snowflake_ext.go | 40 +++++++++++++++ .../procedure_describe_snowflake_ext.go | 51 +++++++++++++++++++ pkg/sdk/functions_and_procedures_commons.go | 2 +- pkg/sdk/procedures_ext.go | 8 ++- pkg/sdk/testint/functions_integration_test.go | 28 ++++++++-- .../testint/procedures_integration_test.go | 33 +++++++++++- 6 files changed, 156 insertions(+), 6 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go index d5f09abe3f..92bf1c16e1 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -490,3 +490,43 @@ func (f *FunctionDetailsAssert) HasExactlyExternalAccessIntegrationsNormalizedIn }) return f } + +func (f *FunctionDetailsAssert) ContainsExactlySecrets(secrets map[string]sdk.SchemaObjectIdentifier) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.NormalizedSecrets == nil { + return fmt.Errorf("expected normalized secrets to have value; got: nil") + } + for k, v := range secrets { + if s, ok := o.NormalizedSecrets[k]; !ok { + return fmt.Errorf("expected normalized secrets to have a secret associated with key %s", k) + } else { + if s.FullyQualifiedName() != v.FullyQualifiedName() { + return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) + } + } + } + for k, _ := range o.NormalizedSecrets { + if _, ok := secrets[k]; !ok { + return fmt.Errorf("normalized secrets have unexpected key: %s", k) + } + } + + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasExactlyPackagesInAnyOrder(packages ...string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.NormalizedPackages == nil { + return fmt.Errorf("expected packages to have value; got: nil") + } + if !assert2.ElementsMatch(t, packages, o.NormalizedPackages) { + return fmt.Errorf("expected %v packages, got %v", packages, o.NormalizedPackages) + } + return nil + }) + return f +} diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go index 550c4c5c1f..323762c4c1 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go @@ -476,3 +476,54 @@ func (f *ProcedureDetailsAssert) HasExactlyExternalAccessIntegrationsNormalizedI }) return f } + +func (f *ProcedureDetailsAssert) ContainsExactlySecrets(secrets map[string]sdk.SchemaObjectIdentifier) *ProcedureDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.ProcedureDetails) error { + t.Helper() + if o.NormalizedSecrets == nil { + return fmt.Errorf("expected normalized secrets to have value; got: nil") + } + for k, v := range secrets { + if s, ok := o.NormalizedSecrets[k]; !ok { + return fmt.Errorf("expected normalized secrets to have a secret associated with key %s", k) + } else { + if s.FullyQualifiedName() != v.FullyQualifiedName() { + return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) + } + } + } + for k, _ := range o.NormalizedSecrets { + if _, ok := secrets[k]; !ok { + return fmt.Errorf("normalized secrets have unexpected key: %s", k) + } + } + + return nil + }) + return f +} + +func (f *ProcedureDetailsAssert) HasExactlyPackagesInAnyOrder(packages ...string) *ProcedureDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.ProcedureDetails) error { + t.Helper() + if o.NormalizedPackages == nil { + return fmt.Errorf("expected packages to have value; got: nil") + } + if !assert2.ElementsMatch(t, packages, o.NormalizedPackages) { + return fmt.Errorf("expected %v packages, got %v", packages, o.NormalizedPackages) + } + return nil + }) + return f +} + +func (f *ProcedureDetailsAssert) HasSnowparkVersion(expected string) *ProcedureDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.ProcedureDetails) error { + t.Helper() + if o.SnowparkVersion != expected { + return fmt.Errorf("expected snowpark version %s; got: %s", expected, o.SnowparkVersion) + } + return nil + }) + return f +} diff --git a/pkg/sdk/functions_and_procedures_commons.go b/pkg/sdk/functions_and_procedures_commons.go index 7da4f1e57d..35f4ab3dbb 100644 --- a/pkg/sdk/functions_and_procedures_commons.go +++ b/pkg/sdk/functions_and_procedures_commons.go @@ -128,7 +128,7 @@ func parseFunctionOrProcedureExternalAccessIntegrations(raw string) ([]AccountOb // TODO [before V1]: test func parseFunctionOrProcedurePackages(raw string) ([]string, error) { log.Printf("[DEBUG] external access integrations: %s", raw) - return collections.Map(ParseCommaSeparatedStringArray(raw, false), strings.TrimSpace), nil + return collections.Map(ParseCommaSeparatedStringArray(raw, true), strings.TrimSpace), nil } // TODO [before V1]: unit test diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index 95ad886a8a..59f2b2fbee 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -179,7 +179,13 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) } } } else { - errs = append(errs, fmt.Errorf("could not parse package from Snowflake, expected at least snowpark package, got nil")) + switch strings.ToUpper(v.Language) { + case "JAVA", "SCALA", "PYTHON": + errs = append(errs, fmt.Errorf("could not parse package from Snowflake, expected at least snowpark package, got nil")) + default: + v.NormalizedPackages = []string{} + } + } return v, errors.Join(errs...) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b14aa44441..8aa1b217fb 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -127,6 +127,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersionNil(). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -219,9 +220,8 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnsNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExactlyExternalAccessIntegrations(externalAccessIntegration). - // TODO [SNOW-1348103]: parse to identifier list - // TODO [SNOW-1348103]: check multiple secrets (to know how to parse) HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaFunction.JarName, @@ -229,6 +229,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). HasTargetPath(targetPath). HasNormalizedTargetPath("~", jarName). HasInstalledPackagesNil(). @@ -306,6 +307,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersionNil(). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -388,6 +390,7 @@ func TestInt_Functions(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaFunction.JarName, @@ -395,6 +398,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -540,6 +544,7 @@ func TestInt_Functions(t *testing.T) { HasHandlerNil(). HasRuntimeVersionNil(). HasPackagesNil(). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -618,6 +623,7 @@ func TestInt_Functions(t *testing.T) { HasHandlerNil(). HasRuntimeVersionNil(). HasPackagesNil(). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -692,6 +698,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(funcName). HasRuntimeVersion("3.8"). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -774,6 +781,7 @@ func TestInt_Functions(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpPythonFunction.PythonFileName(), @@ -781,6 +789,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(funcName). HasRuntimeVersion("3.8"). HasPackages(`['absl-py==0.10.0','about-time==4.2.1']`). + HasExactlyPackagesInAnyOrder("absl-py==0.10.0", "about-time==4.2.1"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -855,6 +864,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(tmpPythonFunction.PythonHandler()). HasRuntimeVersion("3.8"). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -934,6 +944,7 @@ func TestInt_Functions(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpPythonFunction.PythonFileName(), @@ -941,6 +952,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(tmpPythonFunction.PythonHandler()). HasRuntimeVersion("3.8"). HasPackages(`['absl-py==0.10.0','about-time==4.2.1']`). + HasExactlyPackagesInAnyOrder("about-time==4.2.1", "absl-py==0.10.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -1016,6 +1028,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1107,6 +1120,7 @@ func TestInt_Functions(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaFunction.JarName, @@ -1114,6 +1128,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). HasTargetPath(targetPath). HasNormalizedTargetPath("~", jarName). HasInstalledPackagesNil(). @@ -1189,6 +1204,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[]`). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1268,6 +1284,7 @@ func TestInt_Functions(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaFunction.JarName, @@ -1275,6 +1292,7 @@ func TestInt_Functions(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1347,6 +1365,7 @@ func TestInt_Functions(t *testing.T) { HasHandlerNil(). HasRuntimeVersionNil(). HasPackagesNil(). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1456,6 +1475,7 @@ func TestInt_Functions(t *testing.T) { HasHandlerNil(). HasRuntimeVersionNil(). HasPackagesNil(). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1525,6 +1545,7 @@ func TestInt_Functions(t *testing.T) { HasHandlerNil(). HasRuntimeVersionNil(). HasPackagesNil(). + HasExactlyPackagesInAnyOrder(). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1628,7 +1649,8 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). - HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), + HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), ) assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) diff --git a/pkg/sdk/testint/procedures_integration_test.go b/pkg/sdk/testint/procedures_integration_test.go index b737d55b22..e8b54a9a4d 100644 --- a/pkg/sdk/testint/procedures_integration_test.go +++ b/pkg/sdk/testint/procedures_integration_test.go @@ -114,6 +114,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0]`). + HasExactlyPackagesInAnyOrder(). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -200,6 +202,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName, @@ -207,6 +210,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:telemetry:0.1.0"). + HasSnowparkVersion("1.14.0"). HasTargetPath(targetPath). HasNormalizedTargetPath("~", jarName). HasInstalledPackagesNil(). @@ -284,6 +289,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:telemetry:0.1.0"). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -361,6 +368,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName, @@ -368,6 +376,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:telemetry:0.1.0"). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -657,6 +667,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(funcName). HasRuntimeVersion("3.8"). HasPackages(`['snowflake-snowpark-python==1.14.0']`). + HasExactlyPackagesInAnyOrder(). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -737,6 +749,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpPythonFunction.PythonFileName(), @@ -744,6 +757,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(funcName). HasRuntimeVersion("3.8"). HasPackages(`['snowflake-snowpark-python==1.14.0','absl-py==0.10.0']`). + HasExactlyPackagesInAnyOrder("absl-py==0.10.0"). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -817,6 +832,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(tmpPythonFunction.PythonHandler()). HasRuntimeVersion("3.8"). HasPackages(`['snowflake-snowpark-python==1.14.0']`). + HasExactlyPackagesInAnyOrder(). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -894,6 +911,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpPythonFunction.PythonFileName(), @@ -901,6 +919,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(tmpPythonFunction.PythonHandler()). HasRuntimeVersion("3.8"). HasPackages(`['snowflake-snowpark-python==1.14.0','absl-py==0.10.0']`). + HasExactlyPackagesInAnyOrder("absl-py==0.10.0"). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNotEmpty(). @@ -976,6 +996,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0]`). + HasExactlyPackagesInAnyOrder(). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1063,6 +1085,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName, @@ -1070,6 +1093,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:telemetry:0.1.0"). + HasSnowparkVersion("1.14.0"). HasTargetPath(targetPath). HasNormalizedTargetPath("~", jarName). HasInstalledPackagesNil(). @@ -1144,6 +1169,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0]`). + HasExactlyPackagesInAnyOrder(). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1223,6 +1250,7 @@ func TestInt_Procedures(t *testing.T) { HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). HasImports(fmt.Sprintf(`[%s]`, tmpJavaProcedure.JarLocation())). HasExactlyImportsNormalizedInAnyOrder(sdk.NormalizedPath{ StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName, @@ -1230,6 +1258,8 @@ func TestInt_Procedures(t *testing.T) { HasHandler(handler). HasRuntimeVersion("2.12"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasExactlyPackagesInAnyOrder("com.snowflake:telemetry:0.1.0"). + HasSnowparkVersion("1.14.0"). HasTargetPathNil(). HasNormalizedTargetPathNil(). HasInstalledPackagesNil(). @@ -1877,7 +1907,8 @@ def filter_by_role(session, table_name, role): assertions.AssertThatObject(t, objectassert.ProcedureDetails(t, id). HasExactlyExternalAccessIntegrations(externalAccessIntegration). HasExactlyExternalAccessIntegrationsNormalizedInAnyOrder(externalAccessIntegration). - HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), + HasExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}). + ContainsExactlySecrets(map[string]sdk.SchemaObjectIdentifier{"abc": secretId}), ) assertParametersSet(t, objectparametersassert.ProcedureParameters(t, id)) From 2e9df0693b24f4bb199d92b4f215547e5fc7a66b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 10:56:16 +0100 Subject: [PATCH 06/10] Add secrets, comment, external access integrations, and packages to function --- .../function_and_procedure_commons.go | 17 ++++- pkg/resources/function_commons.go | 67 +++++++++++++++++++ pkg/resources/function_java.go | 18 +++-- 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/pkg/resources/function_and_procedure_commons.go b/pkg/resources/function_and_procedure_commons.go index e21801813c..0f03be8eea 100644 --- a/pkg/resources/function_and_procedure_commons.go +++ b/pkg/resources/function_and_procedure_commons.go @@ -10,7 +10,7 @@ import ( func readFunctionOrProcedureArguments(d *schema.ResourceData, args []sdk.NormalizedArgument) error { if len(args) == 0 { - // TODO [SNOW-1348103]: handle empty list + // TODO [before V1]: handle empty list return nil } // We do it the unusual way because the default values are not returned by SF. @@ -40,6 +40,21 @@ func readFunctionOrProcedureImports(d *schema.ResourceData, imports []sdk.Normal return d.Set("imports", imps) } +func readFunctionOrProcedureExternalAccessIntegrations(d *schema.ResourceData, externalAccessIntegrations []sdk.AccountObjectIdentifier) error { + return d.Set("externalAccessIntegrations", collections.Map(externalAccessIntegrations, func(id sdk.AccountObjectIdentifier) string { return id.Name() })) +} + +func readFunctionOrProcedureSecrets(d *schema.ResourceData, secrets map[string]sdk.SchemaObjectIdentifier) error { + all := make([]map[string]any, 0) + for k, v := range secrets { + all = append(all, map[string]any{ + "secret_variable_name": k, + "secret_id": v.FullyQualifiedName(), + }) + } + return d.Set("secrets", all) +} + func readFunctionOrProcedureTargetPath(d *schema.ResourceData, normalizedPath *sdk.NormalizedPath) error { if normalizedPath == nil { // don't do anything if imports not present diff --git a/pkg/resources/function_commons.go b/pkg/resources/function_commons.go index fe5a097a45..e5e5326d85 100644 --- a/pkg/resources/function_commons.go +++ b/pkg/resources/function_commons.go @@ -441,6 +441,46 @@ func parseFunctionImportsCommon(d *schema.ResourceData) ([]sdk.FunctionImportReq return imports, nil } +func parseFunctionPackagesCommon(d *schema.ResourceData) ([]sdk.FunctionPackageRequest, error) { + packages := make([]sdk.FunctionPackageRequest, 0) + if v, ok := d.GetOk("packages"); ok { + for _, pkg := range v.(*schema.Set).List() { + packages = append(packages, *sdk.NewFunctionPackageRequest().WithPackage(pkg.(string))) + } + } + return packages, nil +} + +func parseExternalAccessIntegrationsCommon(d *schema.ResourceData) ([]sdk.AccountObjectIdentifier, error) { + integrations := make([]sdk.AccountObjectIdentifier, 0) + if v, ok := d.GetOk("external_access_integrations"); ok { + for _, i := range v.(*schema.Set).List() { + id, err := sdk.ParseAccountObjectIdentifier(i.(string)) + if err != nil { + return nil, err + } + integrations = append(integrations, id) + } + } + return integrations, nil +} + +func parseSecretsCommon(d *schema.ResourceData) ([]sdk.SecretReference, error) { + secretReferences := make([]sdk.SecretReference, 0) + if v, ok := d.GetOk("secrets"); ok { + for _, s := range v.(*schema.Set).List() { + name := s.(map[string]any)["secret_variable_name"].(string) + idRaw := s.(map[string]any)["secret_id"].(string) + id, err := sdk.ParseSchemaObjectIdentifier(idRaw) + if err != nil { + return nil, err + } + secretReferences = append(secretReferences, sdk.SecretReference{VariableName: name, Name: id}) + } + } + return secretReferences, nil +} + func parseFunctionTargetPathCommon(d *schema.ResourceData) (string, error) { var tp string if v, ok := d.GetOk("target_path"); ok { @@ -482,6 +522,33 @@ func setFunctionImportsInBuilder[T any](d *schema.ResourceData, setImports func( return nil } +func setFunctionPackagesInBuilder[T any](d *schema.ResourceData, setPackages func([]sdk.FunctionPackageRequest) T) error { + packages, err := parseFunctionPackagesCommon(d) + if err != nil { + return err + } + setPackages(packages) + return nil +} + +func setExternalAccessIntegrationsInBuilder[T any](d *schema.ResourceData, setIntegrations func([]sdk.AccountObjectIdentifier) T) error { + integrations, err := parseExternalAccessIntegrationsCommon(d) + if err != nil { + return err + } + setIntegrations(integrations) + return nil +} + +func setSecretsInBuilder[T any](d *schema.ResourceData, setSecrets func([]sdk.SecretReference) T) error { + secrets, err := parseSecretsCommon(d) + if err != nil { + return err + } + setSecrets(secrets) + return nil +} + func setFunctionTargetPathInBuilder[T any](d *schema.ResourceData, setTargetPath func(string) T) error { tp, err := parseFunctionTargetPathCommon(d) if err != nil { diff --git a/pkg/resources/function_java.go b/pkg/resources/function_java.go index c8fca3c13f..a34fb4ea8a 100644 --- a/pkg/resources/function_java.go +++ b/pkg/resources/function_java.go @@ -72,12 +72,11 @@ func CreateContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta attributeMappedValueCreateBuilder[string](d, "null_input_behavior", request.WithNullInputBehavior, sdk.ToNullInputBehavior), attributeMappedValueCreateBuilder[string](d, "return_results_behavior", request.WithReturnResultsBehavior, sdk.ToReturnResultsBehavior), stringAttributeCreateBuilder(d, "runtime_version", request.WithRuntimeVersion), - // TODO [SNOW-1348103]: handle the rest of the attributes - // comment + stringAttributeCreateBuilder(d, "comment", request.WithComment), setFunctionImportsInBuilder(d, request.WithImports), - // packages - // external_access_integrations - // secrets + setFunctionPackagesInBuilder(d, request.WithPackages), + setExternalAccessIntegrationsInBuilder(d, request.WithExternalAccessIntegrations), + setSecretsInBuilder(d, request.WithSecrets), setFunctionTargetPathInBuilder(d, request.WithTargetPath), stringAttributeCreateBuilder(d, "function_definition", request.WithFunctionDefinitionWrapped), ) @@ -121,19 +120,18 @@ func ReadContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta a // TODO [SNOW-1348103]: handle setting state to value from config errs := errors.Join( - // TODO [SNOW-1348103]: set the rest of the fields // 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) setOptionalFromStringPtr(d, "runtime_version", allFunctionDetails.functionDetails.RuntimeVersion), - // comment + d.Set("comment", allFunctionDetails.function.Description), readFunctionOrProcedureImports(d, allFunctionDetails.functionDetails.NormalizedImports), - // packages + d.Set("packages", allFunctionDetails.functionDetails.NormalizedPackages), setRequiredFromStringPtr(d, "handler", allFunctionDetails.functionDetails.Handler), - // external_access_integrations - // secrets + readFunctionOrProcedureExternalAccessIntegrations(d, allFunctionDetails.functionDetails.NormalizedExternalAccessIntegrations), + readFunctionOrProcedureSecrets(d, allFunctionDetails.functionDetails.NormalizedSecrets), readFunctionOrProcedureTargetPath(d, allFunctionDetails.functionDetails.NormalizedTargetPath), setOptionalFromStringPtr(d, "function_definition", allFunctionDetails.functionDetails.Body), d.Set("function_language", allFunctionDetails.functionDetails.Language), From 269e513ea7b99b40075b6d729e72df4ca46bdee2 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 11:04:42 +0100 Subject: [PATCH 07/10] Handle updates partially --- pkg/resources/function_java.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/resources/function_java.go b/pkg/resources/function_java.go index a34fb4ea8a..8dc97f4adc 100644 --- a/pkg/resources/function_java.go +++ b/pkg/resources/function_java.go @@ -171,11 +171,16 @@ func UpdateContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta setRequest := sdk.NewFunctionSetRequest() unsetRequest := sdk.NewFunctionUnsetRequest() + err = errors.Join( + stringAttributeUpdate(d, "comment", &setRequest.Comment, &unsetRequest.Comment), + ) + if err != nil { + return diag.FromErr(err) + } + // TODO [SNOW-1348103]: handle all updates - // secure // external access integration // secrets - // comment if updateParamDiags := handleFunctionParametersUpdate(d, setRequest, unsetRequest); len(updateParamDiags) > 0 { return updateParamDiags @@ -185,15 +190,38 @@ func UpdateContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta if !reflect.DeepEqual(*setRequest, *sdk.NewFunctionSetRequest()) { err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*setRequest)) if err != nil { + d.Partial(true) return diag.FromErr(err) } } if !reflect.DeepEqual(*unsetRequest, *sdk.NewFunctionUnsetRequest()) { err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*unsetRequest)) if err != nil { + d.Partial(true) return diag.FromErr(err) } } + // has to be handled separately + if d.HasChange("is_secure") { + if v := d.Get("is_secure").(string); v != BooleanDefault { + parsed, err := booleanStringToBool(v) + if err != nil { + return diag.FromErr(err) + } + err = client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetSecure(parsed)) + if err != nil { + d.Partial(true) + return diag.FromErr(err) + } + } else { + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetSecure(true)) + if err != nil { + d.Partial(true) + return diag.FromErr(err) + } + } + } + return ReadContextFunctionJava(ctx, d, meta) } From 0712213e01af152405bdfe5da1420da9bc6cc2c0 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 12:03:16 +0100 Subject: [PATCH 08/10] Test function --- .../config/model/function_java_model_ext.go | 53 ++++++++++- .../function_and_procedure_commons.go | 2 +- pkg/resources/function_commons.go | 1 - pkg/resources/function_java.go | 24 ++++- .../function_java_acceptance_test.go | 88 ++++++++++++++++++- 5 files changed, 157 insertions(+), 11 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go index 8579ea981a..5f106b6ab1 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go @@ -2,9 +2,11 @@ package model import ( "encoding/json" + "strings" tfconfig "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" ) @@ -69,13 +71,62 @@ func (f *FunctionJavaModel) WithImport(stageLocation string, pathOnStage string) return f.WithImportsValue( tfconfig.ObjectVariable( map[string]tfconfig.Variable{ - "stage_location": tfconfig.StringVariable(stageLocation), + "stage_location": tfconfig.StringVariable(strings.TrimPrefix(stageLocation, "@")), "path_on_stage": tfconfig.StringVariable(pathOnStage), }, ), ) } +func (f *FunctionJavaModel) WithImports(imports ...sdk.NormalizedPath) *FunctionJavaModel { + return f.WithImportsValue( + tfconfig.SetVariable( + collections.Map(imports, func(imp sdk.NormalizedPath) tfconfig.Variable { + return tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(imp.StageLocation), + "path_on_stage": tfconfig.StringVariable(imp.PathOnStage), + }, + ) + })..., + ), + ) +} + +func (f *FunctionJavaModel) WithPackages(pkgs ...string) *FunctionJavaModel { + return f.WithPackagesValue( + tfconfig.SetVariable( + collections.Map(pkgs, func(pkg string) tfconfig.Variable { return tfconfig.StringVariable(pkg) })..., + ), + ) +} + +func (f *FunctionJavaModel) WithExternalAccessIntegrations(ids ...sdk.AccountObjectIdentifier) *FunctionJavaModel { + return f.WithExternalAccessIntegrationsValue( + tfconfig.SetVariable( + collections.Map(ids, func(id sdk.AccountObjectIdentifier) tfconfig.Variable { return tfconfig.StringVariable(id.Name()) })..., + ), + ) +} + +func (f *FunctionJavaModel) WithSecrets(secrets map[string]sdk.SchemaObjectIdentifier) *FunctionJavaModel { + var objects []tfconfig.Variable + for k, v := range secrets { + objects = append(objects, tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "secret_variable_name": tfconfig.StringVariable(k), + "secret_id": tfconfig.StringVariable(v.FullyQualifiedName()), + }, + )) + } + + return f.WithSecretsValue( + tfconfig.SetVariable( + objects..., + ), + ) +} + func (f *FunctionJavaModel) WithTargetPathParts(stageLocation string, pathOnStage string) *FunctionJavaModel { return f.WithTargetPathValue( tfconfig.ObjectVariable( diff --git a/pkg/resources/function_and_procedure_commons.go b/pkg/resources/function_and_procedure_commons.go index 0f03be8eea..0a7f3acdb0 100644 --- a/pkg/resources/function_and_procedure_commons.go +++ b/pkg/resources/function_and_procedure_commons.go @@ -41,7 +41,7 @@ func readFunctionOrProcedureImports(d *schema.ResourceData, imports []sdk.Normal } func readFunctionOrProcedureExternalAccessIntegrations(d *schema.ResourceData, externalAccessIntegrations []sdk.AccountObjectIdentifier) error { - return d.Set("externalAccessIntegrations", collections.Map(externalAccessIntegrations, func(id sdk.AccountObjectIdentifier) string { return id.Name() })) + return d.Set("external_access_integrations", collections.Map(externalAccessIntegrations, func(id sdk.AccountObjectIdentifier) string { return id.Name() })) } func readFunctionOrProcedureSecrets(d *schema.ResourceData, secrets map[string]sdk.SchemaObjectIdentifier) error { diff --git a/pkg/resources/function_commons.go b/pkg/resources/function_commons.go index e5e5326d85..526f090f8c 100644 --- a/pkg/resources/function_commons.go +++ b/pkg/resources/function_commons.go @@ -316,7 +316,6 @@ func functionBaseSchema() map[string]schema.Schema { ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), }, Optional: true, - ForceNew: true, Description: "The names of [external access integrations](https://docs.snowflake.com/en/sql-reference/sql/create-external-access-integration) needed in order for this function’s handler code to access external networks. An external access integration specifies [network rules](https://docs.snowflake.com/en/sql-reference/sql/create-network-rule) and [secrets](https://docs.snowflake.com/en/sql-reference/sql/create-secret) that specify external locations and credentials (if any) allowed for use by handler code when making requests of an external network, such as an external REST API.", }, "secrets": { diff --git a/pkg/resources/function_java.go b/pkg/resources/function_java.go index 8dc97f4adc..31664b5416 100644 --- a/pkg/resources/function_java.go +++ b/pkg/resources/function_java.go @@ -173,15 +173,31 @@ func UpdateContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta err = errors.Join( stringAttributeUpdate(d, "comment", &setRequest.Comment, &unsetRequest.Comment), + func() error { + if d.HasChange("secrets") { + return setSecretsInBuilder(d, func(references []sdk.SecretReference) *sdk.FunctionSetRequest { + return setRequest.WithSecretsList(sdk.SecretsListRequest{SecretsList: references}) + }) + } + return nil + }(), + func() error { + if d.HasChange("external_access_integrations") { + return setExternalAccessIntegrationsInBuilder(d, func(references []sdk.AccountObjectIdentifier) any { + if references == nil || len(references) == 0 { + return unsetRequest.WithExternalAccessIntegrations(true) + } else { + return setRequest.WithExternalAccessIntegrations(references) + } + }) + } + return nil + }(), ) if err != nil { return diag.FromErr(err) } - // TODO [SNOW-1348103]: handle all updates - // external access integration - // secrets - if updateParamDiags := handleFunctionParametersUpdate(d, setRequest, unsetRequest); len(updateParamDiags) > 0 { return updateParamDiags } diff --git a/pkg/resources/function_java_acceptance_test.go b/pkg/resources/function_java_acceptance_test.go index b805187b69..9b8f032779 100644 --- a/pkg/resources/function_java_acceptance_test.go +++ b/pkg/resources/function_java_acceptance_test.go @@ -173,6 +173,27 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { stage, stageCleanup := acc.TestClient().Stage.CreateStage(t) t.Cleanup(stageCleanup) + secretId := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + secretId2 := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + + networkRule, networkRuleCleanup := acc.TestClient().NetworkRule.Create(t) + t.Cleanup(networkRuleCleanup) + + secret, secretCleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId, "test_secret_string") + t.Cleanup(secretCleanup) + + secret2, secret2Cleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId2, "test_secret_string_2") + t.Cleanup(secret2Cleanup) + + externalAccessIntegration, externalAccessIntegrationCleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) + t.Cleanup(externalAccessIntegrationCleanup) + + externalAccessIntegration2, externalAccessIntegration2Cleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret2.ID()) + t.Cleanup(externalAccessIntegration2Cleanup) + + tmpJavaFunction := acc.TestClient().CreateSampleJavaFunctionAndJarOnUserStage(t) + tmpJavaFunction2 := acc.TestClient().CreateSampleJavaFunctionAndJarOnUserStage(t) + className := "TestFunc" funcName := "echoVarchar" argName := "x" @@ -187,8 +208,34 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { functionModel := model.FunctionJavaBasicInline("w", id, dataType, handler, definition). WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction2.JarName}, + ). + WithPackages("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration, externalAccessIntegration2). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "abc": secretId, + "def": secretId2, + }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). - WithRuntimeVersion("11") + WithRuntimeVersion("11"). + WithComment("some comment") + + functionModelUpdateWithoutRecreation := model.FunctionJavaBasicInline("w", id, dataType, handler, definition). + WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction2.JarName}, + ). + WithPackages("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "def": secretId2, + }). + WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). + WithRuntimeVersion("11"). + WithComment("some other comment") resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -198,25 +245,58 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { PreCheck: func() { acc.TestAccPreCheck(t) }, CheckDestroy: acc.CheckDestroy(t, resources.FunctionJava), Steps: []resource.TestStep{ - // CREATE BASIC + // CREATE WITH ALL { Config: config.FromModels(t, functionModel), Check: assert.AssertThat(t, resourceassert.FunctionJavaResource(t, functionModel.ResourceReference()). HasNameString(id.Name()). HasIsSecureString(r.BooleanDefault). - HasCommentString(sdk.DefaultFunctionComment). - HasImportsLength(0). + HasImportsLength(2). HasRuntimeVersionString("11"). HasFunctionDefinitionString(definition). + HasCommentString("some comment"). HasFunctionLanguageString("JAVA"). HasFullyQualifiedNameString(id.FullyQualifiedName()), assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "secrets.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "external_access_integrations.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "packages.#", "2")), resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). HasIsSecure(false), ), }, + // UPDATE WITHOUT RECREATION + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(functionModelUpdateWithoutRecreation.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: config.FromModels(t, functionModelUpdateWithoutRecreation), + Check: assert.AssertThat(t, + resourceassert.FunctionJavaResource(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanDefault). + HasImportsLength(2). + HasRuntimeVersionString("11"). + HasFunctionDefinitionString(definition). + HasCommentString("some other comment"). + HasFunctionLanguageString("JAVA"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_variable_name", "def")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_id", secretId2.FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.0", externalAccessIntegration.Name())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "packages.#", "2")), + resourceshowoutputassert.FunctionShowOutput(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasIsSecure(false), + ), + }, }, }) } From 0c8e2140bc08cecf6640b764adb155dd949e5edf Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 12:17:54 +0100 Subject: [PATCH 09/10] Remove task relations --- .../assert/objectassert/procedure_describe_snowflake_ext.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go index 323762c4c1..ef04c3abc2 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go @@ -401,7 +401,7 @@ func (f *ProcedureDetailsAssert) HasExactlyImportsNormalizedInAnyOrder(imports . return fmt.Errorf("expected imports to have value; got: nil") } if !assert2.ElementsMatch(t, imports, o.NormalizedImports) { - return fmt.Errorf("expected %v imports in task relations, got %v", imports, o.NormalizedImports) + return fmt.Errorf("expected %v imports, got %v", imports, o.NormalizedImports) } return nil }) From af4fb226f983f88bf87a28b0b497c444680b09ba Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 12 Dec 2024 12:51:33 +0100 Subject: [PATCH 10/10] Add missing to procedure --- .../function_describe_snowflake_ext.go | 8 +- .../procedure_describe_snowflake_ext.go | 8 +- .../config/model/function_java_model_ext.go | 2 +- .../config/model/procedure_java_model_ext.go | 51 +++++++++++ .../function_and_procedure_commons.go | 48 ++++++++++ pkg/resources/function_commons.go | 48 ---------- pkg/resources/function_java.go | 2 +- pkg/resources/procedure_commons.go | 16 +++- pkg/resources/procedure_java.go | 57 ++++++++---- .../procedure_java_acceptance_test.go | 89 ++++++++++++++++++- pkg/sdk/procedures_ext.go | 1 - 11 files changed, 245 insertions(+), 85 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go index 92bf1c16e1..b00dad3d3c 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -500,13 +500,11 @@ func (f *FunctionDetailsAssert) ContainsExactlySecrets(secrets map[string]sdk.Sc for k, v := range secrets { if s, ok := o.NormalizedSecrets[k]; !ok { return fmt.Errorf("expected normalized secrets to have a secret associated with key %s", k) - } else { - if s.FullyQualifiedName() != v.FullyQualifiedName() { - return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) - } + } else if s.FullyQualifiedName() != v.FullyQualifiedName() { + return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) } } - for k, _ := range o.NormalizedSecrets { + for k := range o.NormalizedSecrets { if _, ok := secrets[k]; !ok { return fmt.Errorf("normalized secrets have unexpected key: %s", k) } diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go index ef04c3abc2..962d34f2d6 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/procedure_describe_snowflake_ext.go @@ -486,13 +486,11 @@ func (f *ProcedureDetailsAssert) ContainsExactlySecrets(secrets map[string]sdk.S for k, v := range secrets { if s, ok := o.NormalizedSecrets[k]; !ok { return fmt.Errorf("expected normalized secrets to have a secret associated with key %s", k) - } else { - if s.FullyQualifiedName() != v.FullyQualifiedName() { - return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) - } + } else if s.FullyQualifiedName() != v.FullyQualifiedName() { + return fmt.Errorf("expected secret with key %s to have id %s, got %s", k, v.FullyQualifiedName(), s.FullyQualifiedName()) } } - for k, _ := range o.NormalizedSecrets { + for k := range o.NormalizedSecrets { if _, ok := secrets[k]; !ok { return fmt.Errorf("normalized secrets have unexpected key: %s", k) } diff --git a/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go index 5f106b6ab1..0174c4dc10 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_java_model_ext.go @@ -110,7 +110,7 @@ func (f *FunctionJavaModel) WithExternalAccessIntegrations(ids ...sdk.AccountObj } func (f *FunctionJavaModel) WithSecrets(secrets map[string]sdk.SchemaObjectIdentifier) *FunctionJavaModel { - var objects []tfconfig.Variable + objects := make([]tfconfig.Variable, 0) for k, v := range secrets { objects = append(objects, tfconfig.ObjectVariable( map[string]tfconfig.Variable{ diff --git a/pkg/acceptance/bettertestspoc/config/model/procedure_java_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/procedure_java_model_ext.go index cb6779784c..b8a1602f79 100644 --- a/pkg/acceptance/bettertestspoc/config/model/procedure_java_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/procedure_java_model_ext.go @@ -3,6 +3,8 @@ package model import ( "encoding/json" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -77,6 +79,55 @@ func (f *ProcedureJavaModel) WithImport(stageLocation string, pathOnStage string ) } +func (f *ProcedureJavaModel) WithImports(imports ...sdk.NormalizedPath) *ProcedureJavaModel { + return f.WithImportsValue( + tfconfig.SetVariable( + collections.Map(imports, func(imp sdk.NormalizedPath) tfconfig.Variable { + return tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(imp.StageLocation), + "path_on_stage": tfconfig.StringVariable(imp.PathOnStage), + }, + ) + })..., + ), + ) +} + +func (f *ProcedureJavaModel) WithPackages(pkgs ...string) *ProcedureJavaModel { + return f.WithPackagesValue( + tfconfig.SetVariable( + collections.Map(pkgs, func(pkg string) tfconfig.Variable { return tfconfig.StringVariable(pkg) })..., + ), + ) +} + +func (f *ProcedureJavaModel) WithExternalAccessIntegrations(ids ...sdk.AccountObjectIdentifier) *ProcedureJavaModel { + return f.WithExternalAccessIntegrationsValue( + tfconfig.SetVariable( + collections.Map(ids, func(id sdk.AccountObjectIdentifier) tfconfig.Variable { return tfconfig.StringVariable(id.Name()) })..., + ), + ) +} + +func (f *ProcedureJavaModel) WithSecrets(secrets map[string]sdk.SchemaObjectIdentifier) *ProcedureJavaModel { + objects := make([]tfconfig.Variable, 0) + for k, v := range secrets { + objects = append(objects, tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "secret_variable_name": tfconfig.StringVariable(k), + "secret_id": tfconfig.StringVariable(v.FullyQualifiedName()), + }, + )) + } + + return f.WithSecretsValue( + tfconfig.SetVariable( + objects..., + ), + ) +} + func (f *ProcedureJavaModel) WithTargetPathParts(stageLocation string, pathOnStage string) *ProcedureJavaModel { return f.WithTargetPathValue( tfconfig.ObjectVariable( diff --git a/pkg/resources/function_and_procedure_commons.go b/pkg/resources/function_and_procedure_commons.go index 0a7f3acdb0..213217e968 100644 --- a/pkg/resources/function_and_procedure_commons.go +++ b/pkg/resources/function_and_procedure_commons.go @@ -67,3 +67,51 @@ func readFunctionOrProcedureTargetPath(d *schema.ResourceData, normalizedPath *s } return d.Set("target_path", tp) } + +func setExternalAccessIntegrationsInBuilder[T any](d *schema.ResourceData, setIntegrations func([]sdk.AccountObjectIdentifier) T) error { + integrations, err := parseExternalAccessIntegrationsCommon(d) + if err != nil { + return err + } + setIntegrations(integrations) + return nil +} + +func setSecretsInBuilder[T any](d *schema.ResourceData, setSecrets func([]sdk.SecretReference) T) error { + secrets, err := parseSecretsCommon(d) + if err != nil { + return err + } + setSecrets(secrets) + return nil +} + +func parseExternalAccessIntegrationsCommon(d *schema.ResourceData) ([]sdk.AccountObjectIdentifier, error) { + integrations := make([]sdk.AccountObjectIdentifier, 0) + if v, ok := d.GetOk("external_access_integrations"); ok { + for _, i := range v.(*schema.Set).List() { + id, err := sdk.ParseAccountObjectIdentifier(i.(string)) + if err != nil { + return nil, err + } + integrations = append(integrations, id) + } + } + return integrations, nil +} + +func parseSecretsCommon(d *schema.ResourceData) ([]sdk.SecretReference, error) { + secretReferences := make([]sdk.SecretReference, 0) + if v, ok := d.GetOk("secrets"); ok { + for _, s := range v.(*schema.Set).List() { + name := s.(map[string]any)["secret_variable_name"].(string) + idRaw := s.(map[string]any)["secret_id"].(string) + id, err := sdk.ParseSchemaObjectIdentifier(idRaw) + if err != nil { + return nil, err + } + secretReferences = append(secretReferences, sdk.SecretReference{VariableName: name, Name: id}) + } + } + return secretReferences, nil +} diff --git a/pkg/resources/function_commons.go b/pkg/resources/function_commons.go index 526f090f8c..7dddd097e7 100644 --- a/pkg/resources/function_commons.go +++ b/pkg/resources/function_commons.go @@ -450,36 +450,6 @@ func parseFunctionPackagesCommon(d *schema.ResourceData) ([]sdk.FunctionPackageR return packages, nil } -func parseExternalAccessIntegrationsCommon(d *schema.ResourceData) ([]sdk.AccountObjectIdentifier, error) { - integrations := make([]sdk.AccountObjectIdentifier, 0) - if v, ok := d.GetOk("external_access_integrations"); ok { - for _, i := range v.(*schema.Set).List() { - id, err := sdk.ParseAccountObjectIdentifier(i.(string)) - if err != nil { - return nil, err - } - integrations = append(integrations, id) - } - } - return integrations, nil -} - -func parseSecretsCommon(d *schema.ResourceData) ([]sdk.SecretReference, error) { - secretReferences := make([]sdk.SecretReference, 0) - if v, ok := d.GetOk("secrets"); ok { - for _, s := range v.(*schema.Set).List() { - name := s.(map[string]any)["secret_variable_name"].(string) - idRaw := s.(map[string]any)["secret_id"].(string) - id, err := sdk.ParseSchemaObjectIdentifier(idRaw) - if err != nil { - return nil, err - } - secretReferences = append(secretReferences, sdk.SecretReference{VariableName: name, Name: id}) - } - } - return secretReferences, nil -} - func parseFunctionTargetPathCommon(d *schema.ResourceData) (string, error) { var tp string if v, ok := d.GetOk("target_path"); ok { @@ -530,24 +500,6 @@ func setFunctionPackagesInBuilder[T any](d *schema.ResourceData, setPackages fun return nil } -func setExternalAccessIntegrationsInBuilder[T any](d *schema.ResourceData, setIntegrations func([]sdk.AccountObjectIdentifier) T) error { - integrations, err := parseExternalAccessIntegrationsCommon(d) - if err != nil { - return err - } - setIntegrations(integrations) - return nil -} - -func setSecretsInBuilder[T any](d *schema.ResourceData, setSecrets func([]sdk.SecretReference) T) error { - secrets, err := parseSecretsCommon(d) - if err != nil { - return err - } - setSecrets(secrets) - return nil -} - func setFunctionTargetPathInBuilder[T any](d *schema.ResourceData, setTargetPath func(string) T) error { tp, err := parseFunctionTargetPathCommon(d) if err != nil { diff --git a/pkg/resources/function_java.go b/pkg/resources/function_java.go index 31664b5416..e085fc0c97 100644 --- a/pkg/resources/function_java.go +++ b/pkg/resources/function_java.go @@ -184,7 +184,7 @@ func UpdateContextFunctionJava(ctx context.Context, d *schema.ResourceData, meta func() error { if d.HasChange("external_access_integrations") { return setExternalAccessIntegrationsInBuilder(d, func(references []sdk.AccountObjectIdentifier) any { - if references == nil || len(references) == 0 { + if len(references) == 0 { return unsetRequest.WithExternalAccessIntegrations(true) } else { return setRequest.WithExternalAccessIntegrations(references) diff --git a/pkg/resources/procedure_commons.go b/pkg/resources/procedure_commons.go index 759f44f878..addb4f3c30 100644 --- a/pkg/resources/procedure_commons.go +++ b/pkg/resources/procedure_commons.go @@ -256,9 +256,8 @@ func procedureBaseSchema() map[string]schema.Schema { ForceNew: true, }, "comment": { - Type: schema.TypeString, - Optional: true, - // TODO [SNOW-1348103]: handle dynamic comment - this is a workaround for now + Type: schema.TypeString, + Optional: true, Default: "user-defined procedure", Description: "Specifies a comment for the procedure.", }, @@ -307,7 +306,6 @@ func procedureBaseSchema() map[string]schema.Schema { ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), }, Optional: true, - ForceNew: true, Description: "The names of [external access integrations](https://docs.snowflake.com/en/sql-reference/sql/create-external-access-integration) needed in order for this procedure’s handler code to access external networks. An external access integration specifies [network rules](https://docs.snowflake.com/en/sql-reference/sql/create-network-rule) and [secrets](https://docs.snowflake.com/en/sql-reference/sql/create-secret) that specify external locations and credentials (if any) allowed for use by handler code when making requests of an external network, such as an external REST API.", }, "secrets": { @@ -486,6 +484,16 @@ func parseProcedureImportsCommon(d *schema.ResourceData) ([]sdk.ProcedureImportR return imports, nil } +func parseProceduresPackagesCommon(d *schema.ResourceData) ([]sdk.ProcedurePackageRequest, error) { + packages := make([]sdk.ProcedurePackageRequest, 0) + if v, ok := d.GetOk("packages"); ok { + for _, pkg := range v.(*schema.Set).List() { + packages = append(packages, *sdk.NewProcedurePackageRequest(pkg.(string))) + } + } + return packages, nil +} + func parseProcedureTargetPathCommon(d *schema.ResourceData) (string, error) { var tp string if v, ok := d.GetOk("target_path"); ok { diff --git a/pkg/resources/procedure_java.go b/pkg/resources/procedure_java.go index bc4f417144..04fcb0cf1a 100644 --- a/pkg/resources/procedure_java.go +++ b/pkg/resources/procedure_java.go @@ -62,8 +62,12 @@ func CreateContextProcedureJava(ctx context.Context, d *schema.ResourceData, met } handler := d.Get("handler").(string) runtimeVersion := d.Get("runtime_version").(string) - // TODO [this PR]: handle real packages - packages := []sdk.ProcedurePackageRequest{*sdk.NewProcedurePackageRequest("com.snowflake:snowpark:1.14.0")} + + packages, err := parseProceduresPackagesCommon(d) + if err != nil { + return diag.FromErr(err) + } + packages = append(packages, *sdk.NewProcedurePackageRequest(fmt.Sprintf(`%s%s`, sdk.JavaSnowparkPackageString, d.Get("snowpark_package").(string)))) argumentDataTypes := collections.Map(argumentRequests, func(r sdk.ProcedureArgumentRequest) datatypes.DataType { return r.ArgDataType }) id := sdk.NewSchemaObjectIdentifierWithArgumentsNormalized(database, sc, name, argumentDataTypes...) @@ -73,12 +77,10 @@ func CreateContextProcedureJava(ctx context.Context, d *schema.ResourceData, met errs := errors.Join( booleanStringAttributeCreateBuilder(d, "is_secure", request.WithSecure), attributeMappedValueCreateBuilder[string](d, "null_input_behavior", request.WithNullInputBehavior, sdk.ToNullInputBehavior), - // TODO [SNOW-1348103]: handle the rest of the attributes - // comment + stringAttributeCreateBuilder(d, "comment", request.WithComment), setProcedureImportsInBuilder(d, request.WithImports), - // packages - // external_access_integrations - // secrets + setExternalAccessIntegrationsInBuilder(d, request.WithExternalAccessIntegrations), + setSecretsInBuilder(d, request.WithSecrets), setProcedureTargetPathInBuilder(d, request.WithTargetPath), stringAttributeCreateBuilder(d, "procedure_definition", request.WithProcedureDefinitionWrapped), ) @@ -122,18 +124,18 @@ func ReadContextProcedureJava(ctx context.Context, d *schema.ResourceData, meta // TODO [SNOW-1348103]: handle setting state to value from config errs := errors.Join( - // TODO [SNOW-1348103]: set the rest of the fields // not reading is_secure on purpose (handled as external change to show output) readFunctionOrProcedureArguments(d, allProcedureDetails.procedureDetails.NormalizedArguments), d.Set("return_type", allProcedureDetails.procedureDetails.ReturnDataType.ToSql()), // not reading null_input_behavior on purpose (handled as external change to show output) setRequiredFromStringPtr(d, "runtime_version", allProcedureDetails.procedureDetails.RuntimeVersion), - // comment + d.Set("comment", allProcedureDetails.procedure.Description), readFunctionOrProcedureImports(d, allProcedureDetails.procedureDetails.NormalizedImports), - // packages + d.Set("packages", allProcedureDetails.procedureDetails.NormalizedPackages), + d.Set("snowpark_package", allProcedureDetails.procedureDetails.SnowparkVersion), setRequiredFromStringPtr(d, "handler", allProcedureDetails.procedureDetails.Handler), - // external_access_integrations - // secrets + readFunctionOrProcedureExternalAccessIntegrations(d, allProcedureDetails.procedureDetails.NormalizedExternalAccessIntegrations), + readFunctionOrProcedureSecrets(d, allProcedureDetails.procedureDetails.NormalizedSecrets), readFunctionOrProcedureTargetPath(d, allProcedureDetails.procedureDetails.NormalizedTargetPath), setOptionalFromStringPtr(d, "procedure_definition", allProcedureDetails.procedureDetails.Body), d.Set("procedure_language", allProcedureDetails.procedureDetails.Language), @@ -173,11 +175,32 @@ func UpdateContextProcedureJava(ctx context.Context, d *schema.ResourceData, met setRequest := sdk.NewProcedureSetRequest() unsetRequest := sdk.NewProcedureUnsetRequest() - // TODO [SNOW-1348103]: handle all updates - // secure - // external access integration - // secrets - // comment + err = errors.Join( + stringAttributeUpdate(d, "comment", &setRequest.Comment, &unsetRequest.Comment), + func() error { + if d.HasChange("secrets") { + return setSecretsInBuilder(d, func(references []sdk.SecretReference) *sdk.ProcedureSetRequest { + return setRequest.WithSecretsList(sdk.SecretsListRequest{SecretsList: references}) + }) + } + return nil + }(), + func() error { + if d.HasChange("external_access_integrations") { + return setExternalAccessIntegrationsInBuilder(d, func(references []sdk.AccountObjectIdentifier) any { + if len(references) == 0 { + return unsetRequest.WithExternalAccessIntegrations(true) + } else { + return setRequest.WithExternalAccessIntegrations(references) + } + }) + } + return nil + }(), + ) + if err != nil { + return diag.FromErr(err) + } if updateParamDiags := handleProcedureParametersUpdate(d, setRequest, unsetRequest); len(updateParamDiags) > 0 { return updateParamDiags diff --git a/pkg/resources/procedure_java_acceptance_test.go b/pkg/resources/procedure_java_acceptance_test.go index 35bdd401ec..c6d0aba743 100644 --- a/pkg/resources/procedure_java_acceptance_test.go +++ b/pkg/resources/procedure_java_acceptance_test.go @@ -172,6 +172,27 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { stage, stageCleanup := acc.TestClient().Stage.CreateStage(t) t.Cleanup(stageCleanup) + secretId := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + secretId2 := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + + networkRule, networkRuleCleanup := acc.TestClient().NetworkRule.Create(t) + t.Cleanup(networkRuleCleanup) + + secret, secretCleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId, "test_secret_string") + t.Cleanup(secretCleanup) + + secret2, secret2Cleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId2, "test_secret_string_2") + t.Cleanup(secret2Cleanup) + + externalAccessIntegration, externalAccessIntegrationCleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) + t.Cleanup(externalAccessIntegrationCleanup) + + externalAccessIntegration2, externalAccessIntegration2Cleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret2.ID()) + t.Cleanup(externalAccessIntegration2Cleanup) + + tmpJavaProcedure := acc.TestClient().CreateSampleJavaProcedureAndJarOnUserStage(t) + tmpJavaProcedure2 := acc.TestClient().CreateSampleJavaProcedureAndJarOnUserStage(t) + className := "TestFunc" funcName := "echoVarchar" argName := "x" @@ -186,8 +207,36 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { procedureModel := model.ProcedureJavaBasicInline("w", id, dataType, handler, definition). WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaProcedure2.JarName}, + ). + WithSnowparkPackage("1.14.0"). + WithPackages("com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration, externalAccessIntegration2). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "abc": secretId, + "def": secretId2, + }). + WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). + WithRuntimeVersion("11"). + WithComment("some comment") + + procedureModelUpdateWithoutRecreation := model.ProcedureJavaBasicInline("w", id, dataType, handler, definition). + WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaProcedure.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaProcedure2.JarName}, + ). + WithSnowparkPackage("1.14.0"). + WithPackages("com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "def": secretId2, + }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). - WithRuntimeVersion("11") + WithRuntimeVersion("11"). + WithComment("some other comment") resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -204,18 +253,52 @@ func TestAcc_ProcedureJava_InlineFull(t *testing.T) { resourceassert.ProcedureJavaResource(t, procedureModel.ResourceReference()). HasNameString(id.Name()). HasIsSecureString(r.BooleanDefault). - HasCommentString(sdk.DefaultProcedureComment). - HasImportsLength(0). + HasImportsLength(2). HasRuntimeVersionString("11"). HasProcedureDefinitionString(definition). + HasCommentString("some comment"). HasProcedureLanguageString("JAVA"). HasFullyQualifiedNameString(id.FullyQualifiedName()), assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "secrets.#", "2")), + assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "external_access_integrations.#", "2")), + assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "packages.#", "1")), + assert.Check(resource.TestCheckResourceAttr(procedureModel.ResourceReference(), "packages.0", "com.snowflake:telemetry:0.1.0")), resourceshowoutputassert.ProcedureShowOutput(t, procedureModel.ResourceReference()). HasIsSecure(false), ), }, + // UPDATE WITHOUT RECREATION + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(procedureModelUpdateWithoutRecreation.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: config.FromModels(t, procedureModelUpdateWithoutRecreation), + Check: assert.AssertThat(t, + resourceassert.ProcedureJavaResource(t, procedureModelUpdateWithoutRecreation.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanDefault). + HasImportsLength(2). + HasRuntimeVersionString("11"). + HasProcedureDefinitionString(definition). + HasCommentString("some other comment"). + HasProcedureLanguageString("JAVA"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "secrets.#", "1")), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_variable_name", "def")), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_id", secretId2.FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.#", "1")), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.0", externalAccessIntegration.Name())), + assert.Check(resource.TestCheckResourceAttr(procedureModelUpdateWithoutRecreation.ResourceReference(), "packages.#", "1")), + resourceshowoutputassert.ProcedureShowOutput(t, procedureModelUpdateWithoutRecreation.ResourceReference()). + HasIsSecure(false), + ), + }, }, }) } diff --git a/pkg/sdk/procedures_ext.go b/pkg/sdk/procedures_ext.go index 59f2b2fbee..4a308dc97c 100644 --- a/pkg/sdk/procedures_ext.go +++ b/pkg/sdk/procedures_ext.go @@ -185,7 +185,6 @@ func procedureDetailsFromRows(rows []ProcedureDetail) (*ProcedureDetails, error) default: v.NormalizedPackages = []string{} } - } return v, errors.Join(errs...)