Skip to content

Commit

Permalink
feat: Handle missing fields in function and procedure (#3273)
Browse files Browse the repository at this point in the history
Handle secrets, external access integrations, comments, packages, and
snowpark package.
  • Loading branch information
sfc-gh-asawicki authored Dec 12, 2024
1 parent 7197b57 commit 53e7a0a
Showing 16 changed files with 839 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -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,57 @@ 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
}

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
}
Original file line number Diff line number Diff line change
@@ -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
})
@@ -460,3 +460,68 @@ 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
}

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
}
Original file line number Diff line number Diff line change
@@ -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 {
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 *FunctionJavaModel) WithTargetPathParts(stageLocation string, pathOnStage string) *FunctionJavaModel {
return f.WithTargetPathValue(
tfconfig.ObjectVariable(
Original file line number Diff line number Diff line change
@@ -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(
65 changes: 64 additions & 1 deletion pkg/resources/function_and_procedure_commons.go
Original file line number Diff line number Diff line change
@@ -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("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 {
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
@@ -52,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
}
Loading

0 comments on commit 53e7a0a

Please sign in to comment.