Support Bigquery Views (#230)

* Support views in Terraform.BigQuery

* Add tests for Table with view, and fix existing Table test

* Remove dead code

* run gofmt

* Address comments

* Address review comments and add support for use_legacy_sql

* Force transmission/storage of UseLegacySQL

* Trying to fix tests

* add tests for useLegacySQL
This commit is contained in:
James McGill 2017-08-26 04:15:44 -04:00 committed by Dana Hoffman
parent 6715d0d363
commit d23e9c668f
3 changed files with 212 additions and 3 deletions

View File

@ -229,7 +229,6 @@ func resourceBigQueryDatasetRead(d *schema.ResourceData, meta interface{}) error
d.Set("etag", res.Etag) d.Set("etag", res.Etag)
d.Set("labels", res.Labels) d.Set("labels", res.Labels)
d.Set("location", res.Location)
d.Set("self_link", res.SelfLink) d.Set("self_link", res.SelfLink)
d.Set("description", res.Description) d.Set("description", res.Description)
d.Set("friendly_name", res.FriendlyName) d.Set("friendly_name", res.FriendlyName)
@ -238,6 +237,15 @@ func resourceBigQueryDatasetRead(d *schema.ResourceData, meta interface{}) error
d.Set("dataset_id", res.DatasetReference.DatasetId) d.Set("dataset_id", res.DatasetReference.DatasetId)
d.Set("default_table_expiration_ms", res.DefaultTableExpirationMs) d.Set("default_table_expiration_ms", res.DefaultTableExpirationMs)
// Older Tables in BigQuery have no Location set in the API response. This may be an issue when importing
// tables created before BigQuery was available in multiple zones. We can safely assume that these tables
// are in the US, as this was the default at the time.
if res.Location == "" {
d.Set("location", "US")
} else {
d.Set("location", res.Location)
}
return nil return nil
} }

View File

@ -92,6 +92,31 @@ func resourceBigQueryTable() *schema.Resource {
}, },
}, },
// View: [Optional] If specified, configures this table as a view.
"view": &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// Query: [Required] A query that BigQuery executes when the view is
// referenced.
"query": {
Type: schema.TypeString,
Required: true,
},
// UseLegacySQL: [Optional] Specifies whether to use BigQuery's
// legacy SQL for this view. The default value is true. If set to
// false, the view will use BigQuery's standard SQL:
"use_legacy_sql": {
Type: schema.TypeBool,
Optional: true,
},
},
},
},
// TimePartitioning: [Experimental] If specified, configures time-based // TimePartitioning: [Experimental] If specified, configures time-based
// partitioning for this table. // partitioning for this table.
"time_partitioning": &schema.Schema{ "time_partitioning": &schema.Schema{
@ -202,6 +227,10 @@ func resourceTable(d *schema.ResourceData, meta interface{}) (*bigquery.Table, e
}, },
} }
if v, ok := d.GetOk("view"); ok {
table.View = expandView(v)
}
if v, ok := d.GetOk("description"); ok { if v, ok := d.GetOk("description"); ok {
table.Description = v.(string) table.Description = v.(string)
} }
@ -317,6 +346,11 @@ func resourceBigQueryTableRead(d *schema.ResourceData, meta interface{}) error {
d.Set("schema", schema) d.Set("schema", schema)
} }
if res.View != nil {
view := flattenView(res.View)
d.Set("view", view)
}
return nil return nil
} }
@ -394,3 +428,22 @@ func flattenTimePartitioning(tp *bigquery.TimePartitioning) []map[string]interfa
return []map[string]interface{}{result} return []map[string]interface{}{result}
} }
func expandView(configured interface{}) *bigquery.ViewDefinition {
raw := configured.([]interface{})[0].(map[string]interface{})
vd := &bigquery.ViewDefinition{Query: raw["query"].(string)}
if v, ok := raw["use_legacy_sql"]; ok {
vd.UseLegacySql = v.(bool)
vd.ForceSendFields = append(vd.ForceSendFields, "UseLegacySql")
}
return vd
}
func flattenView(vd *bigquery.ViewDefinition) []map[string]interface{} {
result := map[string]interface{}{"query": vd.Query}
result["use_legacy_sql"] = vd.UseLegacySql
return []map[string]interface{}{result}
}

View File

@ -2,6 +2,7 @@ package google
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/acctest"
@ -37,6 +38,53 @@ func TestAccBigQueryTable_Basic(t *testing.T) {
}) })
} }
func TestAccBigQueryTable_View(t *testing.T) {
datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(10))
tableID := fmt.Sprintf("tf_test_%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigQueryTableDestroy,
Steps: []resource.TestStep{
{
Config: testAccBigQueryTableWithView(datasetID, tableID),
Check: resource.ComposeTestCheckFunc(
testAccBigQueryTableExistsWithView(
"google_bigquery_table.test"),
),
},
},
})
}
func TestAccBigQueryTable_ViewWithLegacySQL(t *testing.T) {
datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(10))
tableID := fmt.Sprintf("tf_test_%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckBigQueryTableDestroy,
Steps: []resource.TestStep{
{
Config: testAccBigQueryTableWithView(datasetID, tableID),
Check: resource.ComposeTestCheckFunc(
testAccBigQueryTableExistsWithLegacySql(
"google_bigquery_table.test", true),
),
},
{
Config: testAccBigQueryTableWithNewSqlView(datasetID, tableID),
Check: resource.ComposeTestCheckFunc(
testAccBigQueryTableExistsWithLegacySql(
"google_bigquery_table.test", false),
),
},
},
})
}
func testAccCheckBigQueryTableDestroy(s *terraform.State) error { func testAccCheckBigQueryTableDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "google_bigquery_table" { if rs.Type != "google_bigquery_table" {
@ -44,7 +92,7 @@ func testAccCheckBigQueryTableDestroy(s *terraform.State) error {
} }
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)
_, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["name"]).Do() _, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["table_id"]).Do()
if err == nil { if err == nil {
return fmt.Errorf("Table still present") return fmt.Errorf("Table still present")
} }
@ -64,11 +112,69 @@ func testAccBigQueryTableExists(n string) resource.TestCheckFunc {
return fmt.Errorf("No ID is set") return fmt.Errorf("No ID is set")
} }
config := testAccProvider.Meta().(*Config) config := testAccProvider.Meta().(*Config)
_, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["name"]).Do() table, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["table_id"]).Do()
if err != nil { if err != nil {
return fmt.Errorf("BigQuery Table not present") return fmt.Errorf("BigQuery Table not present")
} }
if !strings.HasSuffix(table.Id, rs.Primary.Attributes["table_id"]) {
return fmt.Errorf("BigQuery Table ID does not match expected value")
}
return nil
}
}
func testAccBigQueryTableExistsWithView(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
table, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["table_id"]).Do()
if err != nil {
return fmt.Errorf("BigQuery Table not present")
}
if table.View == nil {
return fmt.Errorf("View object missing on table")
}
return nil
}
}
func testAccBigQueryTableExistsWithLegacySql(n string, useLegacySql bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
table, err := config.clientBigQuery.Tables.Get(config.Project, rs.Primary.Attributes["dataset_id"], rs.Primary.Attributes["table_id"]).Do()
if err != nil {
return fmt.Errorf("BigQuery Table not present")
}
if table.View == nil {
return fmt.Errorf("View object missing on table")
}
if table.View.UseLegacySql != useLegacySql {
return fmt.Errorf("Value of UseLegacySQL does not match expected value")
}
return nil return nil
} }
} }
@ -114,6 +220,48 @@ EOH
}`, datasetID, tableID) }`, datasetID, tableID)
} }
func testAccBigQueryTableWithView(datasetID, tableID string) string {
return fmt.Sprintf(`
resource "google_bigquery_dataset" "test" {
dataset_id = "%s"
}
resource "google_bigquery_table" "test" {
table_id = "%s"
dataset_id = "${google_bigquery_dataset.test.dataset_id}"
time_partitioning {
type = "DAY"
}
view {
query = "SELECT state FROM [lookerdata:cdc.project_tycho_reports]"
use_legacy_sql = true
}
}`, datasetID, tableID)
}
func testAccBigQueryTableWithNewSqlView(datasetID, tableID string) string {
return fmt.Sprintf(`
resource "google_bigquery_dataset" "test" {
dataset_id = "%s"
}
resource "google_bigquery_table" "test" {
table_id = "%s"
dataset_id = "${google_bigquery_dataset.test.dataset_id}"
time_partitioning {
type = "DAY"
}
view {
query = "%s"
use_legacy_sql = false
}
}`, datasetID, tableID, "SELECT state FROM `lookerdata:cdc.project_tycho_reports`")
}
func testAccBigQueryTableUpdated(datasetID, tableID string) string { func testAccBigQueryTableUpdated(datasetID, tableID string) string {
return fmt.Sprintf(` return fmt.Sprintf(`
resource "google_bigquery_dataset" "test" { resource "google_bigquery_dataset" "test" {