Skip to content

Commit

Permalink
feat: Added Functions (UDF) Resource & Datasource (#647)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisIsidora committed Aug 18, 2021
1 parent 633f2bb commit f28c7dc
Show file tree
Hide file tree
Showing 13 changed files with 1,282 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/resources/function_grant.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ resource snowflake_function_grant grant {

### Optional

- **arguments** (Block List) List of the arguments for the function (must be present if function_name is present) (see [below for nested schema](#nestedblock--arguments))
- **arguments** (Block List) List of the arguments for the function (must be present if function has arguments and function_name is present) (see [below for nested schema](#nestedblock--arguments))
- **function_name** (String) The name of the function on which to grant privileges immediately (only valid if on_future is false).
- **id** (String) The ID of this resource.
- **on_future** (Boolean) When this is set to true and a schema_name is provided, apply this grant on all future functions in the given schema. When this is true and no schema_name is provided apply this grant on all future functions in the given database. The function_name, arguments, return_type, and shares fields must be unset in order to use on_future.
Expand Down
4 changes: 4 additions & 0 deletions examples/data-sources/snowflake_functions/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
data "snowflake_functions" "current" {
database = "MYDB"
schema = "MYSCHEMA"
}
108 changes: 108 additions & 0 deletions pkg/datasources/functions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package datasources

import (
"database/sql"
"fmt"
"log"

"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

var functionsSchema = map[string]*schema.Schema{
"database": {
Type: schema.TypeString,
Required: true,
Description: "The database from which to return the schemas from.",
},
"schema": {
Type: schema.TypeString,
Required: true,
Description: "The schema from which to return the functions from.",
},
"functions": {
Type: schema.TypeList,
Computed: true,
Description: "The functions in the schema",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
},
"database": {
Type: schema.TypeString,
Computed: true,
},
"schema": {
Type: schema.TypeString,
Computed: true,
},
"comment": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"argument_types": {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Computed: true,
},
"return_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
},
},
}

func Functions() *schema.Resource {
return &schema.Resource{
Read: ReadFunctions,
Schema: functionsSchema,
}
}

func ReadFunctions(d *schema.ResourceData, meta interface{}) error {
db := meta.(*sql.DB)
databaseName := d.Get("database").(string)
schemaName := d.Get("schema").(string)

currentFunctions, err := snowflake.ListFunctions(databaseName, schemaName, db)
if err == sql.ErrNoRows {
// If not found, mark resource to be removed from statefile during apply or refresh
log.Printf("[DEBUG] functions in schema (%s) not found", d.Id())
d.SetId("")
return nil
} else if err != nil {
log.Printf("[DEBUG] unable to parse functions in schema (%s)", d.Id())
d.SetId("")
return nil
}

functions := []map[string]interface{}{}

for _, function := range currentFunctions {
functionMap := map[string]interface{}{}

functionSignatureMap, err := parseArguments(function.Arguments.String)
if err != nil {
return err
}

functionMap["name"] = function.Name.String
functionMap["database"] = function.DatabaseName.String
functionMap["schema"] = function.SchemaName.String
functionMap["comment"] = function.Comment.String
functionMap["argument_types"] = functionSignatureMap["argumentTypes"].([]string)
functionMap["return_type"] = functionSignatureMap["returnType"].(string)

functions = append(functions, functionMap)
}

d.SetId(fmt.Sprintf(`%v|%v`, databaseName, schemaName))
return d.Set("functions", functions)
}
73 changes: 73 additions & 0 deletions pkg/datasources/functions_acceptance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package datasources_test

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccFunctions(t *testing.T) {
databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
functionName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
functionWithArgumentsName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
resource.ParallelTest(t, resource.TestCase{
Providers: providers(),
Steps: []resource.TestStep{
{
Config: functions(databaseName, schemaName, functionName, functionWithArgumentsName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.snowflake_functions.t", "database", databaseName),
resource.TestCheckResourceAttr("data.snowflake_functions.t", "schema", schemaName),
resource.TestCheckResourceAttrSet("data.snowflake_functions.t", "functions.#"),
resource.TestCheckResourceAttr("data.snowflake_functions.t", "functions.#", "2"),
),
},
},
})
}

func functions(databaseName string, schemaName string, functionName string, functionWithArgumentsName string) string {
s := `
resource "snowflake_database" "test_database" {
name = "%v"
comment = "Terraform acceptance test"
}
resource "snowflake_schema" "test_schema" {
name = "%v"
database = snowflake_database.test_database.name
comment = "Terraform acceptance test"
}
resource "snowflake_function" "test_funct_simple" {
name = "%s"
database = snowflake_database.test_database.name
schema = snowflake_schema.test_schema.name
return_type = "float"
statement = "3.141592654::FLOAT"
}
resource "snowflake_function" "test_funct" {
name = "%s"
database = snowflake_database.test_database.name
schema = snowflake_schema.test_schema.name
arguments {
name = "arg1"
type = "varchar"
}
comment = "Terraform acceptance test"
return_type = "varchar"
language = "javascript"
statement = "var X=3\nreturn X"
}
data snowflake_functions "t" {
database = snowflake_database.test_database.name
schema = snowflake_schema.test_schema.name
depends_on = [snowflake_function.test_funct_simple, snowflake_function.test_funct]
}
`
return fmt.Sprintf(s, databaseName, schemaName, functionName, functionWithArgumentsName)
}
2 changes: 2 additions & 0 deletions pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ func getResources() map[string]*schema.Resource {
"snowflake_database": resources.Database(),
"snowflake_external_function": resources.ExternalFunction(),
"snowflake_file_format": resources.FileFormat(),
"snowflake_function": resources.Function(),
"snowflake_managed_account": resources.ManagedAccount(),
"snowflake_masking_policy": resources.MaskingPolicy(),
"snowflake_materialized_view": resources.MaterializedView(),
Expand Down Expand Up @@ -219,6 +220,7 @@ func getDataSources() map[string]*schema.Resource {
"snowflake_resource_monitors": datasources.ResourceMonitors(),
"snowflake_storage_integrations": datasources.StorageIntegrations(),
"snowflake_row_access_policies": datasources.RowAccessPolicies(),
"snowflake_functions": datasources.Functions(),
"snowflake_procedures": datasources.Procedures(),
}

Expand Down

0 comments on commit f28c7dc

Please sign in to comment.