Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Handle missing fields in function and procedure #3273

Merged
merged 10 commits into from
Dec 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down Expand Up @@ -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
Expand Up @@ -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
})
Expand Down Expand Up @@ -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
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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(
Expand Down
65 changes: 64 additions & 1 deletion pkg/resources/function_and_procedure_commons.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Loading