diff --git a/.changelog/6689.txt b/.changelog/6689.txt new file mode 100644 index 00000000000..858074b45d4 --- /dev/null +++ b/.changelog/6689.txt @@ -0,0 +1,12 @@ +```release-note:new-resource +google_bigquery_analytics_hub_listing GA only +``` +```release-note:new-resource +google_bigquery_analytics_hub_listing_iam_* GA only +``` +```release-note:new-resource +google_bigquery_analytics_hub_data_exchange GA only +``` +```release-note:new-resource +google_bigquery_analytics_hub_data_exchange_iam_* GA only +``` diff --git a/google/config.go b/google/config.go index 3ccdeb9fab4..9f511813c8f 100644 --- a/google/config.go +++ b/google/config.go @@ -178,6 +178,7 @@ type Config struct { AppEngineBasePath string ArtifactRegistryBasePath string BigQueryBasePath string + BigqueryAnalyticsHubBasePath string BigqueryConnectionBasePath string BigqueryDataTransferBasePath string BigqueryReservationBasePath string @@ -274,6 +275,7 @@ const ApigeeBasePathKey = "Apigee" const AppEngineBasePathKey = "AppEngine" const ArtifactRegistryBasePathKey = "ArtifactRegistry" const BigQueryBasePathKey = "BigQuery" +const BigqueryAnalyticsHubBasePathKey = "BigqueryAnalyticsHub" const BigqueryConnectionBasePathKey = "BigqueryConnection" const BigqueryDataTransferBasePathKey = "BigqueryDataTransfer" const BigqueryReservationBasePathKey = "BigqueryReservation" @@ -364,6 +366,7 @@ var DefaultBasePaths = map[string]string{ AppEngineBasePathKey: "https://appengine.googleapis.com/v1/", ArtifactRegistryBasePathKey: "https://artifactregistry.googleapis.com/v1/", BigQueryBasePathKey: "https://bigquery.googleapis.com/bigquery/v2/", + BigqueryAnalyticsHubBasePathKey: "https://analyticshub.googleapis.com/v1/", BigqueryConnectionBasePathKey: "https://bigqueryconnection.googleapis.com/v1/", BigqueryDataTransferBasePathKey: "https://bigquerydatatransfer.googleapis.com/v1/", BigqueryReservationBasePathKey: "https://bigqueryreservation.googleapis.com/v1/", @@ -1216,6 +1219,7 @@ func ConfigureBasePaths(c *Config) { c.AppEngineBasePath = DefaultBasePaths[AppEngineBasePathKey] c.ArtifactRegistryBasePath = DefaultBasePaths[ArtifactRegistryBasePathKey] c.BigQueryBasePath = DefaultBasePaths[BigQueryBasePathKey] + c.BigqueryAnalyticsHubBasePath = DefaultBasePaths[BigqueryAnalyticsHubBasePathKey] c.BigqueryConnectionBasePath = DefaultBasePaths[BigqueryConnectionBasePathKey] c.BigqueryDataTransferBasePath = DefaultBasePaths[BigqueryDataTransferBasePathKey] c.BigqueryReservationBasePath = DefaultBasePaths[BigqueryReservationBasePathKey] diff --git a/google/iam_bigquery_analytics_hub_data_exchange.go b/google/iam_bigquery_analytics_hub_data_exchange.go new file mode 100644 index 00000000000..6c3051e45c7 --- /dev/null +++ b/google/iam_bigquery_analytics_hub_data_exchange.go @@ -0,0 +1,223 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var BigqueryAnalyticsHubDataExchangeIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "location": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "data_exchange_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, +} + +type BigqueryAnalyticsHubDataExchangeIamUpdater struct { + project string + location string + dataExchangeId string + d TerraformResourceData + Config *Config +} + +func BigqueryAnalyticsHubDataExchangeIamUpdaterProducer(d TerraformResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + if err := d.Set("project", project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + } + values["project"] = project + location, _ := getLocation(d, config) + if location != "" { + if err := d.Set("location", location); err != nil { + return nil, fmt.Errorf("Error setting location: %s", err) + } + } + values["location"] = location + if v, ok := d.GetOk("data_exchange_id"); ok { + values["data_exchange_id"] = v.(string) + } + + // We may have gotten either a long or short name, so attempt to parse long name if possible + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("data_exchange_id").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &BigqueryAnalyticsHubDataExchangeIamUpdater{ + project: values["project"], + location: values["location"], + dataExchangeId: values["data_exchange_id"], + d: d, + Config: config, + } + + if err := d.Set("project", u.project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + if err := d.Set("location", u.location); err != nil { + return nil, fmt.Errorf("Error setting location: %s", err) + } + if err := d.Set("data_exchange_id", u.GetResourceId()); err != nil { + return nil, fmt.Errorf("Error setting data_exchange_id: %s", err) + } + + return u, nil +} + +func BigqueryAnalyticsHubDataExchangeIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + values["project"] = project + } + + location, _ := getLocation(d, config) + if location != "" { + values["location"] = location + } + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &BigqueryAnalyticsHubDataExchangeIamUpdater{ + project: values["project"], + location: values["location"], + dataExchangeId: values["data_exchange_id"], + d: d, + Config: config, + } + if err := d.Set("data_exchange_id", u.GetResourceId()); err != nil { + return fmt.Errorf("Error setting data_exchange_id: %s", err) + } + d.SetId(u.GetResourceId()) + return nil +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url, err := u.qualifyDataExchangeUrl("getIamPolicy") + if err != nil { + return nil, err + } + + project, err := getProject(u.d, u.Config) + if err != nil { + return nil, err + } + var obj map[string]interface{} + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return nil, err + } + + policy, err := sendRequest(u.Config, "POST", project, url, userAgent, obj) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url, err := u.qualifyDataExchangeUrl("setIamPolicy") + if err != nil { + return err + } + project, err := getProject(u.d, u.Config) + if err != nil { + return err + } + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return err + } + + _, err = sendRequestWithTimeout(u.Config, "POST", project, url, userAgent, obj, u.d.Timeout(schema.TimeoutCreate)) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) qualifyDataExchangeUrl(methodIdentifier string) (string, error) { + urlTemplate := fmt.Sprintf("{{BigqueryAnalyticsHubBasePath}}%s:%s", fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s", u.project, u.location, u.dataExchangeId), methodIdentifier) + url, err := replaceVars(u.d, u.Config, urlTemplate) + if err != nil { + return "", err + } + return url, nil +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s", u.project, u.location, u.dataExchangeId) +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-bigqueryanalyticshub-dataexchange-%s", u.GetResourceId()) +} + +func (u *BigqueryAnalyticsHubDataExchangeIamUpdater) DescribeResource() string { + return fmt.Sprintf("bigqueryanalyticshub dataexchange %q", u.GetResourceId()) +} diff --git a/google/iam_bigquery_analytics_hub_data_exchange_generated_test.go b/google/iam_bigquery_analytics_hub_data_exchange_generated_test.go new file mode 100644 index 00000000000..3a4037292b2 --- /dev/null +++ b/google/iam_bigquery_analytics_hub_data_exchange_generated_test.go @@ -0,0 +1,220 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccBigqueryAnalyticsHubDataExchangeIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubDataExchangeIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s roles/viewer", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccBigqueryAnalyticsHubDataExchangeIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s roles/viewer", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBigqueryAnalyticsHubDataExchangeIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccBigqueryAnalyticsHubDataExchangeIamMember_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s roles/viewer user:admin@hashicorptest.com", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBigqueryAnalyticsHubDataExchangeIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubDataExchangeIamPolicy_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigqueryAnalyticsHubDataExchangeIamPolicy_emptyBinding(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccBigqueryAnalyticsHubDataExchangeIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_data_exchange_iam_member" "foo" { + project = google_bigquery_analytics_hub_data_exchange.data_exchange.project + location = google_bigquery_analytics_hub_data_exchange.data_exchange.location + data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccBigqueryAnalyticsHubDataExchangeIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_bigquery_analytics_hub_data_exchange_iam_policy" "foo" { + project = google_bigquery_analytics_hub_data_exchange.data_exchange.project + location = google_bigquery_analytics_hub_data_exchange.data_exchange.location + data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccBigqueryAnalyticsHubDataExchangeIamPolicy_emptyBinding(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +data "google_iam_policy" "foo" { +} + +resource "google_bigquery_analytics_hub_data_exchange_iam_policy" "foo" { + project = google_bigquery_analytics_hub_data_exchange.data_exchange.project + location = google_bigquery_analytics_hub_data_exchange.data_exchange.location + data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccBigqueryAnalyticsHubDataExchangeIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_data_exchange_iam_binding" "foo" { + project = google_bigquery_analytics_hub_data_exchange.data_exchange.project + location = google_bigquery_analytics_hub_data_exchange.data_exchange.location + data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccBigqueryAnalyticsHubDataExchangeIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_data_exchange_iam_binding" "foo" { + project = google_bigquery_analytics_hub_data_exchange.data_exchange.project + location = google_bigquery_analytics_hub_data_exchange.data_exchange.location + data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:gterraformtest1@gmail.com"] +} +`, context) +} diff --git a/google/iam_bigquery_analytics_hub_listing.go b/google/iam_bigquery_analytics_hub_listing.go new file mode 100644 index 00000000000..9299c7826e7 --- /dev/null +++ b/google/iam_bigquery_analytics_hub_listing.go @@ -0,0 +1,238 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var BigqueryAnalyticsHubListingIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "location": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "data_exchange_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "listing_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, +} + +type BigqueryAnalyticsHubListingIamUpdater struct { + project string + location string + dataExchangeId string + listingId string + d TerraformResourceData + Config *Config +} + +func BigqueryAnalyticsHubListingIamUpdaterProducer(d TerraformResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + if err := d.Set("project", project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + } + values["project"] = project + location, _ := getLocation(d, config) + if location != "" { + if err := d.Set("location", location); err != nil { + return nil, fmt.Errorf("Error setting location: %s", err) + } + } + values["location"] = location + if v, ok := d.GetOk("data_exchange_id"); ok { + values["data_exchange_id"] = v.(string) + } + + if v, ok := d.GetOk("listing_id"); ok { + values["listing_id"] = v.(string) + } + + // We may have gotten either a long or short name, so attempt to parse long name if possible + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)/listings/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("listing_id").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &BigqueryAnalyticsHubListingIamUpdater{ + project: values["project"], + location: values["location"], + dataExchangeId: values["data_exchange_id"], + listingId: values["listing_id"], + d: d, + Config: config, + } + + if err := d.Set("project", u.project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + if err := d.Set("location", u.location); err != nil { + return nil, fmt.Errorf("Error setting location: %s", err) + } + if err := d.Set("data_exchange_id", u.dataExchangeId); err != nil { + return nil, fmt.Errorf("Error setting data_exchange_id: %s", err) + } + if err := d.Set("listing_id", u.GetResourceId()); err != nil { + return nil, fmt.Errorf("Error setting listing_id: %s", err) + } + + return u, nil +} + +func BigqueryAnalyticsHubListingIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + values["project"] = project + } + + location, _ := getLocation(d, config) + if location != "" { + values["location"] = location + } + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)/listings/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &BigqueryAnalyticsHubListingIamUpdater{ + project: values["project"], + location: values["location"], + dataExchangeId: values["data_exchange_id"], + listingId: values["listing_id"], + d: d, + Config: config, + } + if err := d.Set("listing_id", u.GetResourceId()); err != nil { + return fmt.Errorf("Error setting listing_id: %s", err) + } + d.SetId(u.GetResourceId()) + return nil +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url, err := u.qualifyListingUrl("getIamPolicy") + if err != nil { + return nil, err + } + + project, err := getProject(u.d, u.Config) + if err != nil { + return nil, err + } + var obj map[string]interface{} + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return nil, err + } + + policy, err := sendRequest(u.Config, "POST", project, url, userAgent, obj) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url, err := u.qualifyListingUrl("setIamPolicy") + if err != nil { + return err + } + project, err := getProject(u.d, u.Config) + if err != nil { + return err + } + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return err + } + + _, err = sendRequestWithTimeout(u.Config, "POST", project, url, userAgent, obj, u.d.Timeout(schema.TimeoutCreate)) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) qualifyListingUrl(methodIdentifier string) (string, error) { + urlTemplate := fmt.Sprintf("{{BigqueryAnalyticsHubBasePath}}%s:%s", fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s", u.project, u.location, u.dataExchangeId, u.listingId), methodIdentifier) + url, err := replaceVars(u.d, u.Config, urlTemplate) + if err != nil { + return "", err + } + return url, nil +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s", u.project, u.location, u.dataExchangeId, u.listingId) +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-bigqueryanalyticshub-listing-%s", u.GetResourceId()) +} + +func (u *BigqueryAnalyticsHubListingIamUpdater) DescribeResource() string { + return fmt.Sprintf("bigqueryanalyticshub listing %q", u.GetResourceId()) +} diff --git a/google/iam_bigquery_analytics_hub_listing_generated_test.go b/google/iam_bigquery_analytics_hub_listing_generated_test.go new file mode 100644 index 00000000000..4cf79da6284 --- /dev/null +++ b/google/iam_bigquery_analytics_hub_listing_generated_test.go @@ -0,0 +1,320 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccBigqueryAnalyticsHubListingIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubListingIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s roles/viewer", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"]), fmt.Sprintf("tf_test_my_listing%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccBigqueryAnalyticsHubListingIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s roles/viewer", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"]), fmt.Sprintf("tf_test_my_listing%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBigqueryAnalyticsHubListingIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccBigqueryAnalyticsHubListingIamMember_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s roles/viewer user:admin@hashicorptest.com", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"]), fmt.Sprintf("tf_test_my_listing%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBigqueryAnalyticsHubListingIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubListingIamPolicy_basicGenerated(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"]), fmt.Sprintf("tf_test_my_listing%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigqueryAnalyticsHubListingIamPolicy_emptyBinding(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/dataExchanges/%s/listings/%s", getTestProjectFromEnv(), "US", fmt.Sprintf("tf_test_my_data_exchange%s", context["random_suffix"]), fmt.Sprintf("tf_test_my_listing%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccBigqueryAnalyticsHubListingIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} + +resource "google_bigquery_analytics_hub_listing_iam_member" "foo" { + project = google_bigquery_analytics_hub_listing.listing.project + location = google_bigquery_analytics_hub_listing.listing.location + data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id + listing_id = google_bigquery_analytics_hub_listing.listing.listing_id + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccBigqueryAnalyticsHubListingIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_bigquery_analytics_hub_listing_iam_policy" "foo" { + project = google_bigquery_analytics_hub_listing.listing.project + location = google_bigquery_analytics_hub_listing.listing.location + data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id + listing_id = google_bigquery_analytics_hub_listing.listing.listing_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccBigqueryAnalyticsHubListingIamPolicy_emptyBinding(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} + +data "google_iam_policy" "foo" { +} + +resource "google_bigquery_analytics_hub_listing_iam_policy" "foo" { + project = google_bigquery_analytics_hub_listing.listing.project + location = google_bigquery_analytics_hub_listing.listing.location + data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id + listing_id = google_bigquery_analytics_hub_listing.listing.listing_id + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccBigqueryAnalyticsHubListingIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} + +resource "google_bigquery_analytics_hub_listing_iam_binding" "foo" { + project = google_bigquery_analytics_hub_listing.listing.project + location = google_bigquery_analytics_hub_listing.listing.location + data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id + listing_id = google_bigquery_analytics_hub_listing.listing.listing_id + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccBigqueryAnalyticsHubListingIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} + +resource "google_bigquery_analytics_hub_listing_iam_binding" "foo" { + project = google_bigquery_analytics_hub_listing.listing.project + location = google_bigquery_analytics_hub_listing.listing.location + data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id + listing_id = google_bigquery_analytics_hub_listing.listing.listing_id + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:gterraformtest1@gmail.com"] +} +`, context) +} diff --git a/google/provider.go b/google/provider.go index 0c89f8a2ca8..bc53c03f590 100644 --- a/google/provider.go +++ b/google/provider.go @@ -205,6 +205,14 @@ func Provider() *schema.Provider { "GOOGLE_BIG_QUERY_CUSTOM_ENDPOINT", }, DefaultBasePaths[BigQueryBasePathKey]), }, + "bigquery_analytics_hub_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_BIGQUERY_ANALYTICS_HUB_CUSTOM_ENDPOINT", + }, DefaultBasePaths[BigqueryAnalyticsHubBasePathKey]), + }, "bigquery_connection_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -901,9 +909,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 240 -// Generated IAM resources: 141 -// Total generated resources: 381 +// Generated resources: 242 +// Generated IAM resources: 147 +// Total generated resources: 389 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -957,6 +965,14 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_bigquery_table_iam_member": ResourceIamMember(BigQueryTableIamSchema, BigQueryTableIamUpdaterProducer, BigQueryTableIdParseFunc), "google_bigquery_table_iam_policy": ResourceIamPolicy(BigQueryTableIamSchema, BigQueryTableIamUpdaterProducer, BigQueryTableIdParseFunc), "google_bigquery_routine": resourceBigQueryRoutine(), + "google_bigquery_analytics_hub_data_exchange": resourceBigqueryAnalyticsHubDataExchange(), + "google_bigquery_analytics_hub_data_exchange_iam_binding": ResourceIamBinding(BigqueryAnalyticsHubDataExchangeIamSchema, BigqueryAnalyticsHubDataExchangeIamUpdaterProducer, BigqueryAnalyticsHubDataExchangeIdParseFunc), + "google_bigquery_analytics_hub_data_exchange_iam_member": ResourceIamMember(BigqueryAnalyticsHubDataExchangeIamSchema, BigqueryAnalyticsHubDataExchangeIamUpdaterProducer, BigqueryAnalyticsHubDataExchangeIdParseFunc), + "google_bigquery_analytics_hub_data_exchange_iam_policy": ResourceIamPolicy(BigqueryAnalyticsHubDataExchangeIamSchema, BigqueryAnalyticsHubDataExchangeIamUpdaterProducer, BigqueryAnalyticsHubDataExchangeIdParseFunc), + "google_bigquery_analytics_hub_listing": resourceBigqueryAnalyticsHubListing(), + "google_bigquery_analytics_hub_listing_iam_binding": ResourceIamBinding(BigqueryAnalyticsHubListingIamSchema, BigqueryAnalyticsHubListingIamUpdaterProducer, BigqueryAnalyticsHubListingIdParseFunc), + "google_bigquery_analytics_hub_listing_iam_member": ResourceIamMember(BigqueryAnalyticsHubListingIamSchema, BigqueryAnalyticsHubListingIamUpdaterProducer, BigqueryAnalyticsHubListingIdParseFunc), + "google_bigquery_analytics_hub_listing_iam_policy": ResourceIamPolicy(BigqueryAnalyticsHubListingIamSchema, BigqueryAnalyticsHubListingIamUpdaterProducer, BigqueryAnalyticsHubListingIdParseFunc), "google_bigquery_connection": resourceBigqueryConnectionConnection(), "google_bigquery_connection_iam_binding": ResourceIamBinding(BigqueryConnectionConnectionIamSchema, BigqueryConnectionConnectionIamUpdaterProducer, BigqueryConnectionConnectionIdParseFunc), "google_bigquery_connection_iam_member": ResourceIamMember(BigqueryConnectionConnectionIamSchema, BigqueryConnectionConnectionIamUpdaterProducer, BigqueryConnectionConnectionIdParseFunc), @@ -1514,6 +1530,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.AppEngineBasePath = d.Get("app_engine_custom_endpoint").(string) config.ArtifactRegistryBasePath = d.Get("artifact_registry_custom_endpoint").(string) config.BigQueryBasePath = d.Get("big_query_custom_endpoint").(string) + config.BigqueryAnalyticsHubBasePath = d.Get("bigquery_analytics_hub_custom_endpoint").(string) config.BigqueryConnectionBasePath = d.Get("bigquery_connection_custom_endpoint").(string) config.BigqueryDataTransferBasePath = d.Get("bigquery_data_transfer_custom_endpoint").(string) config.BigqueryReservationBasePath = d.Get("bigquery_reservation_custom_endpoint").(string) diff --git a/google/resource_bigquery_analytics_hub_data_exchange.go b/google/resource_bigquery_analytics_hub_data_exchange.go new file mode 100644 index 00000000000..dc646a38a9d --- /dev/null +++ b/google/resource_bigquery_analytics_hub_data_exchange.go @@ -0,0 +1,455 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceBigqueryAnalyticsHubDataExchange() *schema.Resource { + return &schema.Resource{ + Create: resourceBigqueryAnalyticsHubDataExchangeCreate, + Read: resourceBigqueryAnalyticsHubDataExchangeRead, + Update: resourceBigqueryAnalyticsHubDataExchangeUpdate, + Delete: resourceBigqueryAnalyticsHubDataExchangeDelete, + + Importer: &schema.ResourceImporter{ + State: resourceBigqueryAnalyticsHubDataExchangeImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "data_exchange_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The ID of the data exchange. Must contain only Unicode letters, numbers (0-9), underscores (_). Should not use characters that require URL-escaping, or characters outside of ASCII, spaces.`, + }, + "display_name": { + Type: schema.TypeString, + Required: true, + Description: `Human-readable display name of the data exchange. The display name must contain only Unicode letters, numbers (0-9), underscores (_), dashes (-), spaces ( ), and must not start or end with spaces.`, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The name of the location this data exchange.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `Description of the data exchange.`, + }, + "documentation": { + Type: schema.TypeString, + Optional: true, + Description: `Documentation describing the data exchange.`, + }, + "icon": { + Type: schema.TypeString, + Optional: true, + Description: `Base64 encoded image representing the data exchange.`, + }, + "primary_contact": { + Type: schema.TypeString, + Optional: true, + Description: `Email or URL of the primary point of contact of the data exchange.`, + }, + "listing_count": { + Type: schema.TypeInt, + Computed: true, + Description: `Number of listings contained in the data exchange.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The resource name of the data exchange, for example: +"projects/myproject/locations/US/dataExchanges/123"`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceBigqueryAnalyticsHubDataExchangeCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + displayNameProp, err := expandBigqueryAnalyticsHubDataExchangeDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandBigqueryAnalyticsHubDataExchangeDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + primaryContactProp, err := expandBigqueryAnalyticsHubDataExchangePrimaryContact(d.Get("primary_contact"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("primary_contact"); !isEmptyValue(reflect.ValueOf(primaryContactProp)) && (ok || !reflect.DeepEqual(v, primaryContactProp)) { + obj["primaryContact"] = primaryContactProp + } + documentationProp, err := expandBigqueryAnalyticsHubDataExchangeDocumentation(d.Get("documentation"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("documentation"); !isEmptyValue(reflect.ValueOf(documentationProp)) && (ok || !reflect.DeepEqual(v, documentationProp)) { + obj["documentation"] = documentationProp + } + iconProp, err := expandBigqueryAnalyticsHubDataExchangeIcon(d.Get("icon"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icon"); !isEmptyValue(reflect.ValueOf(iconProp)) && (ok || !reflect.DeepEqual(v, iconProp)) { + obj["icon"] = iconProp + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges?data_exchange_id={{data_exchange_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new DataExchange: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DataExchange: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating DataExchange: %s", err) + } + if err := d.Set("name", flattenBigqueryAnalyticsHubDataExchangeName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating DataExchange %q: %#v", d.Id(), res) + + return resourceBigqueryAnalyticsHubDataExchangeRead(d, meta) +} + +func resourceBigqueryAnalyticsHubDataExchangeRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DataExchange: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("BigqueryAnalyticsHubDataExchange %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + + if err := d.Set("name", flattenBigqueryAnalyticsHubDataExchangeName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("display_name", flattenBigqueryAnalyticsHubDataExchangeDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("description", flattenBigqueryAnalyticsHubDataExchangeDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("primary_contact", flattenBigqueryAnalyticsHubDataExchangePrimaryContact(res["primaryContact"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("documentation", flattenBigqueryAnalyticsHubDataExchangeDocumentation(res["documentation"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("listing_count", flattenBigqueryAnalyticsHubDataExchangeListingCount(res["listingCount"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + if err := d.Set("icon", flattenBigqueryAnalyticsHubDataExchangeIcon(res["icon"], d, config)); err != nil { + return fmt.Errorf("Error reading DataExchange: %s", err) + } + + return nil +} + +func resourceBigqueryAnalyticsHubDataExchangeUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DataExchange: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + displayNameProp, err := expandBigqueryAnalyticsHubDataExchangeDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandBigqueryAnalyticsHubDataExchangeDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + primaryContactProp, err := expandBigqueryAnalyticsHubDataExchangePrimaryContact(d.Get("primary_contact"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("primary_contact"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, primaryContactProp)) { + obj["primaryContact"] = primaryContactProp + } + documentationProp, err := expandBigqueryAnalyticsHubDataExchangeDocumentation(d.Get("documentation"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("documentation"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, documentationProp)) { + obj["documentation"] = documentationProp + } + iconProp, err := expandBigqueryAnalyticsHubDataExchangeIcon(d.Get("icon"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icon"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, iconProp)) { + obj["icon"] = iconProp + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating DataExchange %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("display_name") { + updateMask = append(updateMask, "displayName") + } + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("primary_contact") { + updateMask = append(updateMask, "primaryContact") + } + + if d.HasChange("documentation") { + updateMask = append(updateMask, "documentation") + } + + if d.HasChange("icon") { + updateMask = append(updateMask, "icon") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating DataExchange %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating DataExchange %q: %#v", d.Id(), res) + } + + return resourceBigqueryAnalyticsHubDataExchangeRead(d, meta) +} + +func resourceBigqueryAnalyticsHubDataExchangeDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for DataExchange: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting DataExchange %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "DataExchange") + } + + log.Printf("[DEBUG] Finished deleting DataExchange %q: %#v", d.Id(), res) + return nil +} + +func resourceBigqueryAnalyticsHubDataExchangeImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenBigqueryAnalyticsHubDataExchangeName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubDataExchangeDisplayName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubDataExchangeDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubDataExchangePrimaryContact(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubDataExchangeDocumentation(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubDataExchangeListingCount(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := stringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenBigqueryAnalyticsHubDataExchangeIcon(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandBigqueryAnalyticsHubDataExchangeDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubDataExchangeDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubDataExchangePrimaryContact(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubDataExchangeDocumentation(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubDataExchangeIcon(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_bigquery_analytics_hub_data_exchange_generated_test.go b/google/resource_bigquery_analytics_hub_data_exchange_generated_test.go new file mode 100644 index 00000000000..5f3778cd3ea --- /dev/null +++ b/google/resource_bigquery_analytics_hub_data_exchange_generated_test.go @@ -0,0 +1,93 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccBigqueryAnalyticsHubDataExchange_bigqueryAnalyticshubDataExchangeBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigqueryAnalyticsHubDataExchangeDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubDataExchange_bigqueryAnalyticshubDataExchangeBasicExample(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_data_exchange.data_exchange", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"data_exchange_id", "location"}, + }, + }, + }) +} + +func testAccBigqueryAnalyticsHubDataExchange_bigqueryAnalyticshubDataExchangeBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} +`, context) +} + +func testAccCheckBigqueryAnalyticsHubDataExchangeDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_bigquery_analytics_hub_data_exchange" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("BigqueryAnalyticsHubDataExchange still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_bigquery_analytics_hub_data_exchange_sweeper_test.go b/google/resource_bigquery_analytics_hub_data_exchange_sweeper_test.go new file mode 100644 index 00000000000..64afe90a227 --- /dev/null +++ b/google/resource_bigquery_analytics_hub_data_exchange_sweeper_test.go @@ -0,0 +1,128 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("BigqueryAnalyticsHubDataExchange", &resource.Sweeper{ + Name: "BigqueryAnalyticsHubDataExchange", + F: testSweepBigqueryAnalyticsHubDataExchange, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepBigqueryAnalyticsHubDataExchange(region string) error { + resourceName := "BigqueryAnalyticsHubDataExchange" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://analyticshub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/dataExchanges", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["dataExchanges"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + var name string + // Id detected in the delete URL, attempt to use id. + if obj["id"] != nil { + name = GetResourceNameFromSelfLink(obj["id"].(string)) + } else if obj["name"] != nil { + name = GetResourceNameFromSelfLink(obj["name"].(string)) + } else { + log.Printf("[INFO][SWEEPER_LOG] %s resource name and id were nil", resourceName) + return nil + } + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://analyticshub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/resource_bigquery_analytics_hub_listing.go b/google/resource_bigquery_analytics_hub_listing.go new file mode 100644 index 00000000000..c05bf839189 --- /dev/null +++ b/google/resource_bigquery_analytics_hub_listing.go @@ -0,0 +1,768 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceBigqueryAnalyticsHubListing() *schema.Resource { + return &schema.Resource{ + Create: resourceBigqueryAnalyticsHubListingCreate, + Read: resourceBigqueryAnalyticsHubListingRead, + Update: resourceBigqueryAnalyticsHubListingUpdate, + Delete: resourceBigqueryAnalyticsHubListingDelete, + + Importer: &schema.ResourceImporter{ + State: resourceBigqueryAnalyticsHubListingImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "bigquery_dataset": { + Type: schema.TypeList, + Required: true, + Description: `Shared dataset i.e. BigQuery dataset source.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: projectNumberDiffSuppress, + Description: `Resource name of the dataset source for this listing. e.g. projects/myproject/datasets/123`, + }, + }, + }, + }, + "data_exchange_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The ID of the data exchange. Must contain only Unicode letters, numbers (0-9), underscores (_). Should not use characters that require URL-escaping, or characters outside of ASCII, spaces.`, + }, + "display_name": { + Type: schema.TypeString, + Required: true, + Description: `Human-readable display name of the listing. The display name must contain only Unicode letters, numbers (0-9), underscores (_), dashes (-), spaces ( ), ampersands (&) and can't start or end with spaces.`, + }, + "listing_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The ID of the listing. Must contain only Unicode letters, numbers (0-9), underscores (_). Should not use characters that require URL-escaping, or characters outside of ASCII, spaces.`, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The name of the location this data exchange listing.`, + }, + "categories": { + Type: schema.TypeList, + Optional: true, + Description: `Categories of the listing. Up to two categories are allowed.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "data_provider": { + Type: schema.TypeList, + Optional: true, + Description: `Details of the data provider who owns the source data.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `Name of the data provider.`, + }, + "primary_contact": { + Type: schema.TypeString, + Optional: true, + Description: `Email or URL of the data provider.`, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: `Short description of the listing. The description must not contain Unicode non-characters and C0 and C1 control codes except tabs (HT), new lines (LF), carriage returns (CR), and page breaks (FF).`, + }, + "documentation": { + Type: schema.TypeString, + Optional: true, + Description: `Documentation describing the listing.`, + }, + "icon": { + Type: schema.TypeString, + Optional: true, + Description: `Base64 encoded image representing the listing.`, + }, + "primary_contact": { + Type: schema.TypeString, + Optional: true, + Description: `Email or URL of the primary point of contact of the listing.`, + }, + "publisher": { + Type: schema.TypeList, + Optional: true, + Description: `Details of the publisher who owns the listing and who can share the source data.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: `Name of the listing publisher.`, + }, + "primary_contact": { + Type: schema.TypeString, + Optional: true, + Description: `Email or URL of the listing publisher.`, + }, + }, + }, + }, + "request_access": { + Type: schema.TypeString, + Optional: true, + Description: `Email or URL of the request access of the listing. Subscribers can use this reference to request access.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The resource name of the listing. e.g. "projects/myproject/locations/US/dataExchanges/123/listings/456"`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceBigqueryAnalyticsHubListingCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + displayNameProp, err := expandBigqueryAnalyticsHubListingDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandBigqueryAnalyticsHubListingDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + primaryContactProp, err := expandBigqueryAnalyticsHubListingPrimaryContact(d.Get("primary_contact"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("primary_contact"); !isEmptyValue(reflect.ValueOf(primaryContactProp)) && (ok || !reflect.DeepEqual(v, primaryContactProp)) { + obj["primaryContact"] = primaryContactProp + } + documentationProp, err := expandBigqueryAnalyticsHubListingDocumentation(d.Get("documentation"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("documentation"); !isEmptyValue(reflect.ValueOf(documentationProp)) && (ok || !reflect.DeepEqual(v, documentationProp)) { + obj["documentation"] = documentationProp + } + iconProp, err := expandBigqueryAnalyticsHubListingIcon(d.Get("icon"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icon"); !isEmptyValue(reflect.ValueOf(iconProp)) && (ok || !reflect.DeepEqual(v, iconProp)) { + obj["icon"] = iconProp + } + requestAccessProp, err := expandBigqueryAnalyticsHubListingRequestAccess(d.Get("request_access"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("request_access"); !isEmptyValue(reflect.ValueOf(requestAccessProp)) && (ok || !reflect.DeepEqual(v, requestAccessProp)) { + obj["requestAccess"] = requestAccessProp + } + dataProviderProp, err := expandBigqueryAnalyticsHubListingDataProvider(d.Get("data_provider"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("data_provider"); !isEmptyValue(reflect.ValueOf(dataProviderProp)) && (ok || !reflect.DeepEqual(v, dataProviderProp)) { + obj["dataProvider"] = dataProviderProp + } + publisherProp, err := expandBigqueryAnalyticsHubListingPublisher(d.Get("publisher"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("publisher"); !isEmptyValue(reflect.ValueOf(publisherProp)) && (ok || !reflect.DeepEqual(v, publisherProp)) { + obj["publisher"] = publisherProp + } + categoriesProp, err := expandBigqueryAnalyticsHubListingCategories(d.Get("categories"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("categories"); !isEmptyValue(reflect.ValueOf(categoriesProp)) && (ok || !reflect.DeepEqual(v, categoriesProp)) { + obj["categories"] = categoriesProp + } + bigqueryDatasetProp, err := expandBigqueryAnalyticsHubListingBigqueryDataset(d.Get("bigquery_dataset"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("bigquery_dataset"); !isEmptyValue(reflect.ValueOf(bigqueryDatasetProp)) && (ok || !reflect.DeepEqual(v, bigqueryDatasetProp)) { + obj["bigqueryDataset"] = bigqueryDatasetProp + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings?listing_id={{listing_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Listing: %#v", obj) + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Listing: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating Listing: %s", err) + } + if err := d.Set("name", flattenBigqueryAnalyticsHubListingName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Listing %q: %#v", d.Id(), res) + + return resourceBigqueryAnalyticsHubListingRead(d, meta) +} + +func resourceBigqueryAnalyticsHubListingRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Listing: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("BigqueryAnalyticsHubListing %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + + if err := d.Set("name", flattenBigqueryAnalyticsHubListingName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("display_name", flattenBigqueryAnalyticsHubListingDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("description", flattenBigqueryAnalyticsHubListingDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("primary_contact", flattenBigqueryAnalyticsHubListingPrimaryContact(res["primaryContact"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("documentation", flattenBigqueryAnalyticsHubListingDocumentation(res["documentation"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("icon", flattenBigqueryAnalyticsHubListingIcon(res["icon"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("request_access", flattenBigqueryAnalyticsHubListingRequestAccess(res["requestAccess"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("data_provider", flattenBigqueryAnalyticsHubListingDataProvider(res["dataProvider"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("publisher", flattenBigqueryAnalyticsHubListingPublisher(res["publisher"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("categories", flattenBigqueryAnalyticsHubListingCategories(res["categories"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + if err := d.Set("bigquery_dataset", flattenBigqueryAnalyticsHubListingBigqueryDataset(res["bigqueryDataset"], d, config)); err != nil { + return fmt.Errorf("Error reading Listing: %s", err) + } + + return nil +} + +func resourceBigqueryAnalyticsHubListingUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Listing: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + displayNameProp, err := expandBigqueryAnalyticsHubListingDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + descriptionProp, err := expandBigqueryAnalyticsHubListingDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + primaryContactProp, err := expandBigqueryAnalyticsHubListingPrimaryContact(d.Get("primary_contact"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("primary_contact"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, primaryContactProp)) { + obj["primaryContact"] = primaryContactProp + } + documentationProp, err := expandBigqueryAnalyticsHubListingDocumentation(d.Get("documentation"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("documentation"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, documentationProp)) { + obj["documentation"] = documentationProp + } + iconProp, err := expandBigqueryAnalyticsHubListingIcon(d.Get("icon"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("icon"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, iconProp)) { + obj["icon"] = iconProp + } + requestAccessProp, err := expandBigqueryAnalyticsHubListingRequestAccess(d.Get("request_access"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("request_access"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, requestAccessProp)) { + obj["requestAccess"] = requestAccessProp + } + dataProviderProp, err := expandBigqueryAnalyticsHubListingDataProvider(d.Get("data_provider"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("data_provider"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, dataProviderProp)) { + obj["dataProvider"] = dataProviderProp + } + publisherProp, err := expandBigqueryAnalyticsHubListingPublisher(d.Get("publisher"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("publisher"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, publisherProp)) { + obj["publisher"] = publisherProp + } + categoriesProp, err := expandBigqueryAnalyticsHubListingCategories(d.Get("categories"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("categories"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, categoriesProp)) { + obj["categories"] = categoriesProp + } + bigqueryDatasetProp, err := expandBigqueryAnalyticsHubListingBigqueryDataset(d.Get("bigquery_dataset"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("bigquery_dataset"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, bigqueryDatasetProp)) { + obj["bigqueryDataset"] = bigqueryDatasetProp + } + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Listing %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("display_name") { + updateMask = append(updateMask, "displayName") + } + + if d.HasChange("description") { + updateMask = append(updateMask, "description") + } + + if d.HasChange("primary_contact") { + updateMask = append(updateMask, "primaryContact") + } + + if d.HasChange("documentation") { + updateMask = append(updateMask, "documentation") + } + + if d.HasChange("icon") { + updateMask = append(updateMask, "icon") + } + + if d.HasChange("request_access") { + updateMask = append(updateMask, "requestAccess") + } + + if d.HasChange("data_provider") { + updateMask = append(updateMask, "dataProvider") + } + + if d.HasChange("publisher") { + updateMask = append(updateMask, "publisher") + } + + if d.HasChange("categories") { + updateMask = append(updateMask, "categories") + } + + if d.HasChange("bigquery_dataset") { + updateMask = append(updateMask, "bigqueryDataset") + } + // updateMask is a URL parameter but not present in the schema, so replaceVars + // won't set it + url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating Listing %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Listing %q: %#v", d.Id(), res) + } + + return resourceBigqueryAnalyticsHubListingRead(d, meta) +} + +func resourceBigqueryAnalyticsHubListingDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := getProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Listing: %s", err) + } + billingProject = project + + url, err := replaceVars(d, config, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Listing %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "Listing") + } + + log.Printf("[DEBUG] Finished deleting Listing %q: %#v", d.Id(), res) + return nil +} + +func resourceBigqueryAnalyticsHubListingImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/dataExchanges/(?P[^/]+)/listings/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenBigqueryAnalyticsHubListingName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingDisplayName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingPrimaryContact(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingDocumentation(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingIcon(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingRequestAccess(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingDataProvider(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["name"] = + flattenBigqueryAnalyticsHubListingDataProviderName(original["name"], d, config) + transformed["primary_contact"] = + flattenBigqueryAnalyticsHubListingDataProviderPrimaryContact(original["primaryContact"], d, config) + return []interface{}{transformed} +} +func flattenBigqueryAnalyticsHubListingDataProviderName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingDataProviderPrimaryContact(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingPublisher(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["name"] = + flattenBigqueryAnalyticsHubListingPublisherName(original["name"], d, config) + transformed["primary_contact"] = + flattenBigqueryAnalyticsHubListingPublisherPrimaryContact(original["primaryContact"], d, config) + return []interface{}{transformed} +} +func flattenBigqueryAnalyticsHubListingPublisherName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingPublisherPrimaryContact(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingCategories(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenBigqueryAnalyticsHubListingBigqueryDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["dataset"] = + flattenBigqueryAnalyticsHubListingBigqueryDatasetDataset(original["dataset"], d, config) + return []interface{}{transformed} +} +func flattenBigqueryAnalyticsHubListingBigqueryDatasetDataset(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandBigqueryAnalyticsHubListingDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingPrimaryContact(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingDocumentation(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingIcon(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingRequestAccess(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingDataProvider(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandBigqueryAnalyticsHubListingDataProviderName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPrimaryContact, err := expandBigqueryAnalyticsHubListingDataProviderPrimaryContact(original["primary_contact"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrimaryContact); val.IsValid() && !isEmptyValue(val) { + transformed["primaryContact"] = transformedPrimaryContact + } + + return transformed, nil +} + +func expandBigqueryAnalyticsHubListingDataProviderName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingDataProviderPrimaryContact(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingPublisher(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandBigqueryAnalyticsHubListingPublisherName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !isEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedPrimaryContact, err := expandBigqueryAnalyticsHubListingPublisherPrimaryContact(original["primary_contact"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPrimaryContact); val.IsValid() && !isEmptyValue(val) { + transformed["primaryContact"] = transformedPrimaryContact + } + + return transformed, nil +} + +func expandBigqueryAnalyticsHubListingPublisherName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingPublisherPrimaryContact(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingCategories(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandBigqueryAnalyticsHubListingBigqueryDataset(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedDataset, err := expandBigqueryAnalyticsHubListingBigqueryDatasetDataset(original["dataset"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDataset); val.IsValid() && !isEmptyValue(val) { + transformed["dataset"] = transformedDataset + } + + return transformed, nil +} + +func expandBigqueryAnalyticsHubListingBigqueryDatasetDataset(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google/resource_bigquery_analytics_hub_listing_generated_test.go b/google/resource_bigquery_analytics_hub_listing_generated_test.go new file mode 100644 index 00000000000..0dc0ddab11e --- /dev/null +++ b/google/resource_bigquery_analytics_hub_listing_generated_test.go @@ -0,0 +1,112 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigqueryAnalyticsHubListingDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingBasicExample(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing.listing", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"data_exchange_id", "listing_id", "location"}, + }, + }, + }) +} + +func testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} +`, context) +} + +func testAccCheckBigqueryAnalyticsHubListingDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_bigquery_analytics_hub_listing" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{BigqueryAnalyticsHubBasePath}}projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = sendRequest(config, "GET", billingProject, url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("BigqueryAnalyticsHubListing still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_bigquery_analytics_hub_listing_sweeper_test.go b/google/resource_bigquery_analytics_hub_listing_sweeper_test.go new file mode 100644 index 00000000000..25c1c92d916 --- /dev/null +++ b/google/resource_bigquery_analytics_hub_listing_sweeper_test.go @@ -0,0 +1,128 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("BigqueryAnalyticsHubListing", &resource.Sweeper{ + Name: "BigqueryAnalyticsHubListing", + F: testSweepBigqueryAnalyticsHubListing, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepBigqueryAnalyticsHubListing(region string) error { + resourceName := "BigqueryAnalyticsHubListing" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://analyticshub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["listings"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + var name string + // Id detected in the delete URL, attempt to use id. + if obj["id"] != nil { + name = GetResourceNameFromSelfLink(obj["id"].(string)) + } else if obj["name"] != nil { + name = GetResourceNameFromSelfLink(obj["name"].(string)) + } else { + log.Printf("[INFO][SWEEPER_LOG] %s resource name and id were nil", resourceName) + return nil + } + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://analyticshub.googleapis.com/v1/projects/{{project}}/locations/{{location}}/dataExchanges/{{data_exchange_id}}/listings/{{listing_id}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/google/resource_bigquery_analytics_hub_listing_test.go b/google/resource_bigquery_analytics_hub_listing_test.go index 71664db3c87..a8b55434fca 100644 --- a/google/resource_bigquery_analytics_hub_listing_test.go +++ b/google/resource_bigquery_analytics_hub_listing_test.go @@ -1 +1,64 @@ package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckBigqueryAnalyticsHubListingDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingBasicExample(context), + }, + { + ResourceName: "google_bigquery_analytics_hub_listing.listing", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingUpdate(context), + }, + }, + }) +} + +func testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingUpdate(context map[string]interface{}) string { + return Nprintf(` +resource "google_bigquery_analytics_hub_data_exchange" "listing" { + location = "US" + data_exchange_id = "tf_test_my_data_exchange%{random_suffix}" + display_name = "tf_test_my_data_exchange%{random_suffix}" + description = "example data exchange%{random_suffix}" +} + +resource "google_bigquery_analytics_hub_listing" "listing" { + location = "US" + data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id + listing_id = "tf_test_my_listing%{random_suffix}" + display_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange update%{random_suffix}" + + bigquery_dataset { + dataset = google_bigquery_dataset.listing.id + } +} + +resource "google_bigquery_dataset" "listing" { + dataset_id = "tf_test_my_listing%{random_suffix}" + friendly_name = "tf_test_my_listing%{random_suffix}" + description = "example data exchange%{random_suffix}" + location = "US" +} +`, context) +} diff --git a/website/docs/r/bigquery_analytics_hub_data_exchange.html.markdown b/website/docs/r/bigquery_analytics_hub_data_exchange.html.markdown index fa3442f122b..f7d5e735e2e 100644 --- a/website/docs/r/bigquery_analytics_hub_data_exchange.html.markdown +++ b/website/docs/r/bigquery_analytics_hub_data_exchange.html.markdown @@ -22,12 +22,10 @@ description: |- A Bigquery Analytics Hub data exchange -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. To get more information about DataExchange, see: -* [API documentation](https://cloud.google.com/bigquery/docs/reference/analytics-hub/rest/v1beta1/projects.locations.dataExchanges) +* [API documentation](https://cloud.google.com/bigquery/docs/reference/analytics-hub/rest/v1/projects.locations.dataExchanges) * How-to Guides * [Official Documentation](https://cloud.google.com/bigquery/docs/analytics-hub-introduction) @@ -41,7 +39,6 @@ To get more information about DataExchange, see: ```hcl resource "google_bigquery_analytics_hub_data_exchange" "data_exchange" { - provider = google-beta location = "US" data_exchange_id = "my_data_exchange" display_name = "my_data_exchange" diff --git a/website/docs/r/bigquery_analytics_hub_data_exchange_iam.html.markdown b/website/docs/r/bigquery_analytics_hub_data_exchange_iam.html.markdown index acf70c901fd..a63ad3eb21c 100644 --- a/website/docs/r/bigquery_analytics_hub_data_exchange_iam.html.markdown +++ b/website/docs/r/bigquery_analytics_hub_data_exchange_iam.html.markdown @@ -30,15 +30,12 @@ Three different resources help you manage your IAM policy for Bigquery Analytics ~> **Note:** `google_bigquery_analytics_hub_data_exchange_iam_binding` resources **can be** used in conjunction with `google_bigquery_analytics_hub_data_exchange_iam_member` resources **only if** they do not grant privilege to the same role. -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. ## google\_bigquery\_analytics\_hub\_data\_exchange\_iam\_policy ```hcl data "google_iam_policy" "admin" { - provider = google-beta binding { role = "roles/viewer" members = [ @@ -48,7 +45,6 @@ data "google_iam_policy" "admin" { } resource "google_bigquery_analytics_hub_data_exchange_iam_policy" "policy" { - provider = google-beta project = google_bigquery_analytics_hub_data_exchange.data_exchange.project location = google_bigquery_analytics_hub_data_exchange.data_exchange.location data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id @@ -60,7 +56,6 @@ resource "google_bigquery_analytics_hub_data_exchange_iam_policy" "policy" { ```hcl resource "google_bigquery_analytics_hub_data_exchange_iam_binding" "binding" { - provider = google-beta project = google_bigquery_analytics_hub_data_exchange.data_exchange.project location = google_bigquery_analytics_hub_data_exchange.data_exchange.location data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id @@ -75,7 +70,6 @@ resource "google_bigquery_analytics_hub_data_exchange_iam_binding" "binding" { ```hcl resource "google_bigquery_analytics_hub_data_exchange_iam_member" "member" { - provider = google-beta project = google_bigquery_analytics_hub_data_exchange.data_exchange.project location = google_bigquery_analytics_hub_data_exchange.data_exchange.location data_exchange_id = google_bigquery_analytics_hub_data_exchange.data_exchange.data_exchange_id diff --git a/website/docs/r/bigquery_analytics_hub_listing.html.markdown b/website/docs/r/bigquery_analytics_hub_listing.html.markdown index 8b43ff280df..478612eb765 100644 --- a/website/docs/r/bigquery_analytics_hub_listing.html.markdown +++ b/website/docs/r/bigquery_analytics_hub_listing.html.markdown @@ -22,12 +22,10 @@ description: |- A Bigquery Analytics Hub data exchange listing -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. To get more information about Listing, see: -* [API documentation](https://cloud.google.com/bigquery/docs/reference/analytics-hub/rest/v1beta1/projects.locations.dataExchanges.listings) +* [API documentation](https://cloud.google.com/bigquery/docs/reference/analytics-hub/rest/v1/projects.locations.dataExchanges.listings) * How-to Guides * [Official Documentation](https://cloud.google.com/bigquery/docs/analytics-hub-introduction) @@ -41,7 +39,6 @@ To get more information about Listing, see: ```hcl resource "google_bigquery_analytics_hub_data_exchange" "listing" { - provider = google-beta location = "US" data_exchange_id = "my_data_exchange" display_name = "my_data_exchange" @@ -49,7 +46,6 @@ resource "google_bigquery_analytics_hub_data_exchange" "listing" { } resource "google_bigquery_analytics_hub_listing" "listing" { - provider = google-beta location = "US" data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id listing_id = "my_listing" @@ -62,7 +58,6 @@ resource "google_bigquery_analytics_hub_listing" "listing" { } resource "google_bigquery_dataset" "listing" { - provider = google-beta dataset_id = "my_listing" friendly_name = "my_listing" description = "example data exchange" diff --git a/website/docs/r/bigquery_analytics_hub_listing_iam.html.markdown b/website/docs/r/bigquery_analytics_hub_listing_iam.html.markdown index 6c28250d5c4..21c77a0efa6 100644 --- a/website/docs/r/bigquery_analytics_hub_listing_iam.html.markdown +++ b/website/docs/r/bigquery_analytics_hub_listing_iam.html.markdown @@ -30,15 +30,12 @@ Three different resources help you manage your IAM policy for Bigquery Analytics ~> **Note:** `google_bigquery_analytics_hub_listing_iam_binding` resources **can be** used in conjunction with `google_bigquery_analytics_hub_listing_iam_member` resources **only if** they do not grant privilege to the same role. -~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. -See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. ## google\_bigquery\_analytics\_hub\_listing\_iam\_policy ```hcl data "google_iam_policy" "admin" { - provider = google-beta binding { role = "roles/viewer" members = [ @@ -48,7 +45,6 @@ data "google_iam_policy" "admin" { } resource "google_bigquery_analytics_hub_listing_iam_policy" "policy" { - provider = google-beta project = google_bigquery_analytics_hub_listing.listing.project location = google_bigquery_analytics_hub_listing.listing.location data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id @@ -61,7 +57,6 @@ resource "google_bigquery_analytics_hub_listing_iam_policy" "policy" { ```hcl resource "google_bigquery_analytics_hub_listing_iam_binding" "binding" { - provider = google-beta project = google_bigquery_analytics_hub_listing.listing.project location = google_bigquery_analytics_hub_listing.listing.location data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id @@ -77,7 +72,6 @@ resource "google_bigquery_analytics_hub_listing_iam_binding" "binding" { ```hcl resource "google_bigquery_analytics_hub_listing_iam_member" "member" { - provider = google-beta project = google_bigquery_analytics_hub_listing.listing.project location = google_bigquery_analytics_hub_listing.listing.location data_exchange_id = google_bigquery_analytics_hub_listing.listing.data_exchange_id