Skip to content

Commit dd12aaa

Browse files
Add Pub/Sub Topic Source Support to Analytics Hub Listing (#14302) (#23334)
[upstream:a6026d82fbfc30149a93d5df6375514c077b1f5b] Signed-off-by: Modular Magician <[email protected]>
1 parent 5c0065b commit dd12aaa

6 files changed

+350
-47
lines changed

.changelog/14302.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
bigqueryanalyticshub: added `pubsub_topic` field to `google_bigquery_analytics_hub_listing` resource
3+
```

google/services/bigqueryanalyticshub/resource_bigquery_analytics_hub_listing.go

Lines changed: 152 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,32 @@ func ResourceBigqueryAnalyticsHubListing() *schema.Resource {
5656
),
5757

5858
Schema: map[string]*schema.Schema{
59+
"data_exchange_id": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
ForceNew: true,
63+
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.`,
64+
},
65+
"display_name": {
66+
Type: schema.TypeString,
67+
Required: true,
68+
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.`,
69+
},
70+
"listing_id": {
71+
Type: schema.TypeString,
72+
Required: true,
73+
ForceNew: true,
74+
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.`,
75+
},
76+
"location": {
77+
Type: schema.TypeString,
78+
Required: true,
79+
ForceNew: true,
80+
Description: `The name of the location this data exchange listing.`,
81+
},
5982
"bigquery_dataset": {
6083
Type: schema.TypeList,
61-
Required: true,
84+
Optional: true,
6285
ForceNew: true,
6386
Description: `Shared dataset i.e. BigQuery dataset source.`,
6487
MaxItems: 1,
@@ -90,29 +113,7 @@ func ResourceBigqueryAnalyticsHubListing() *schema.Resource {
90113
},
91114
},
92115
},
93-
},
94-
"data_exchange_id": {
95-
Type: schema.TypeString,
96-
Required: true,
97-
ForceNew: true,
98-
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.`,
99-
},
100-
"display_name": {
101-
Type: schema.TypeString,
102-
Required: true,
103-
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.`,
104-
},
105-
"listing_id": {
106-
Type: schema.TypeString,
107-
Required: true,
108-
ForceNew: true,
109-
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.`,
110-
},
111-
"location": {
112-
Type: schema.TypeString,
113-
Required: true,
114-
ForceNew: true,
115-
Description: `The name of the location this data exchange listing.`,
116+
ExactlyOneOf: []string{"pubsub_topic", "bigquery_dataset"},
116117
},
117118
"categories": {
118119
Type: schema.TypeList,
@@ -188,6 +189,34 @@ func ResourceBigqueryAnalyticsHubListing() *schema.Resource {
188189
},
189190
},
190191
},
192+
"pubsub_topic": {
193+
Type: schema.TypeList,
194+
Optional: true,
195+
Description: `Pub/Sub topic source.`,
196+
MaxItems: 1,
197+
Elem: &schema.Resource{
198+
Schema: map[string]*schema.Schema{
199+
"topic": {
200+
Type: schema.TypeString,
201+
Required: true,
202+
ForceNew: true,
203+
DiffSuppressFunc: tpgresource.ProjectNumberDiffSuppress,
204+
Description: `Resource name of the Pub/Sub topic source for this listing. e.g. projects/myproject/topics/topicId`,
205+
},
206+
"data_affinity_regions": {
207+
Type: schema.TypeSet,
208+
Optional: true,
209+
Description: `Region hint on where the data might be published. Data affinity regions are modifiable.
210+
See https://cloud.google.com/about/locations for full listing of possible Cloud regions.`,
211+
Elem: &schema.Schema{
212+
Type: schema.TypeString,
213+
},
214+
Set: schema.HashString,
215+
},
216+
},
217+
},
218+
ExactlyOneOf: []string{"pubsub_topic", "bigquery_dataset"},
219+
},
191220
"request_access": {
192221
Type: schema.TypeString,
193222
Optional: true,
@@ -302,6 +331,12 @@ func resourceBigqueryAnalyticsHubListingCreate(d *schema.ResourceData, meta inte
302331
} else if v, ok := d.GetOkExists("bigquery_dataset"); !tpgresource.IsEmptyValue(reflect.ValueOf(bigqueryDatasetProp)) && (ok || !reflect.DeepEqual(v, bigqueryDatasetProp)) {
303332
obj["bigqueryDataset"] = bigqueryDatasetProp
304333
}
334+
pubsubTopicProp, err := expandBigqueryAnalyticsHubListingPubsubTopic(d.Get("pubsub_topic"), d, config)
335+
if err != nil {
336+
return err
337+
} else if v, ok := d.GetOkExists("pubsub_topic"); !tpgresource.IsEmptyValue(reflect.ValueOf(pubsubTopicProp)) && (ok || !reflect.DeepEqual(v, pubsubTopicProp)) {
338+
obj["pubsubTopic"] = pubsubTopicProp
339+
}
305340
restrictedExportConfigProp, err := expandBigqueryAnalyticsHubListingRestrictedExportConfig(d.Get("restricted_export_config"), d, config)
306341
if err != nil {
307342
return err
@@ -436,6 +471,9 @@ func resourceBigqueryAnalyticsHubListingRead(d *schema.ResourceData, meta interf
436471
if err := d.Set("bigquery_dataset", flattenBigqueryAnalyticsHubListingBigqueryDataset(res["bigqueryDataset"], d, config)); err != nil {
437472
return fmt.Errorf("Error reading Listing: %s", err)
438473
}
474+
if err := d.Set("pubsub_topic", flattenBigqueryAnalyticsHubListingPubsubTopic(res["pubsubTopic"], d, config)); err != nil {
475+
return fmt.Errorf("Error reading Listing: %s", err)
476+
}
439477
if err := d.Set("restricted_export_config", flattenBigqueryAnalyticsHubListingRestrictedExportConfig(res["restrictedExportConfig"], d, config)); err != nil {
440478
return fmt.Errorf("Error reading Listing: %s", err)
441479
}
@@ -516,6 +554,12 @@ func resourceBigqueryAnalyticsHubListingUpdate(d *schema.ResourceData, meta inte
516554
} else if v, ok := d.GetOkExists("categories"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, categoriesProp)) {
517555
obj["categories"] = categoriesProp
518556
}
557+
pubsubTopicProp, err := expandBigqueryAnalyticsHubListingPubsubTopic(d.Get("pubsub_topic"), d, config)
558+
if err != nil {
559+
return err
560+
} else if v, ok := d.GetOkExists("pubsub_topic"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, pubsubTopicProp)) {
561+
obj["pubsubTopic"] = pubsubTopicProp
562+
}
519563
restrictedExportConfigProp, err := expandBigqueryAnalyticsHubListingRestrictedExportConfig(d.Get("restricted_export_config"), d, config)
520564
if err != nil {
521565
return err
@@ -568,6 +612,10 @@ func resourceBigqueryAnalyticsHubListingUpdate(d *schema.ResourceData, meta inte
568612
updateMask = append(updateMask, "categories")
569613
}
570614

615+
if d.HasChange("pubsub_topic") {
616+
updateMask = append(updateMask, "pubsubTopic")
617+
}
618+
571619
if d.HasChange("restricted_export_config") {
572620
updateMask = append(updateMask, "restrictedExportConfig")
573621
}
@@ -577,6 +625,25 @@ func resourceBigqueryAnalyticsHubListingUpdate(d *schema.ResourceData, meta inte
577625
if err != nil {
578626
return err
579627
}
628+
if d.HasChange("pubsub_topic.0.data_affinity_regions") {
629+
// Split URL into base and query parts
630+
parts := strings.SplitN(url, "?", 2)
631+
if len(parts) == 2 {
632+
base := parts[0]
633+
query := parts[1]
634+
635+
query = strings.ReplaceAll(query, "%2C", ",")
636+
query = strings.ReplaceAll(query, "%2c", ",")
637+
638+
// Replace "pubsubTopic" with "pubsubTopic.dataAffinityRegions"
639+
query = strings.ReplaceAll(query, "pubsubTopic", "pubsubTopic.dataAffinityRegions")
640+
641+
// Re-encode commas back
642+
query = strings.ReplaceAll(query, ",", "%2C")
643+
644+
url = base + "?" + query
645+
}
646+
}
580647

581648
// err == nil indicates that the billing_project value was found
582649
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
@@ -794,6 +861,32 @@ func flattenBigqueryAnalyticsHubListingBigqueryDatasetSelectedResourcesTable(v i
794861
return v
795862
}
796863

864+
func flattenBigqueryAnalyticsHubListingPubsubTopic(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
865+
if v == nil {
866+
return nil
867+
}
868+
original := v.(map[string]interface{})
869+
if len(original) == 0 {
870+
return nil
871+
}
872+
transformed := make(map[string]interface{})
873+
transformed["topic"] =
874+
flattenBigqueryAnalyticsHubListingPubsubTopicTopic(original["topic"], d, config)
875+
transformed["data_affinity_regions"] =
876+
flattenBigqueryAnalyticsHubListingPubsubTopicDataAffinityRegions(original["dataAffinityRegions"], d, config)
877+
return []interface{}{transformed}
878+
}
879+
func flattenBigqueryAnalyticsHubListingPubsubTopicTopic(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
880+
return v
881+
}
882+
883+
func flattenBigqueryAnalyticsHubListingPubsubTopicDataAffinityRegions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
884+
if v == nil {
885+
return v
886+
}
887+
return schema.NewSet(schema.HashString, v.([]interface{}))
888+
}
889+
797890
func flattenBigqueryAnalyticsHubListingRestrictedExportConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
798891
if v == nil {
799892
return nil
@@ -979,6 +1072,41 @@ func expandBigqueryAnalyticsHubListingBigqueryDatasetSelectedResourcesTable(v in
9791072
return v, nil
9801073
}
9811074

1075+
func expandBigqueryAnalyticsHubListingPubsubTopic(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1076+
l := v.([]interface{})
1077+
if len(l) == 0 || l[0] == nil {
1078+
return nil, nil
1079+
}
1080+
raw := l[0]
1081+
original := raw.(map[string]interface{})
1082+
transformed := make(map[string]interface{})
1083+
1084+
transformedTopic, err := expandBigqueryAnalyticsHubListingPubsubTopicTopic(original["topic"], d, config)
1085+
if err != nil {
1086+
return nil, err
1087+
} else if val := reflect.ValueOf(transformedTopic); val.IsValid() && !tpgresource.IsEmptyValue(val) {
1088+
transformed["topic"] = transformedTopic
1089+
}
1090+
1091+
transformedDataAffinityRegions, err := expandBigqueryAnalyticsHubListingPubsubTopicDataAffinityRegions(original["data_affinity_regions"], d, config)
1092+
if err != nil {
1093+
return nil, err
1094+
} else if val := reflect.ValueOf(transformedDataAffinityRegions); val.IsValid() && !tpgresource.IsEmptyValue(val) {
1095+
transformed["dataAffinityRegions"] = transformedDataAffinityRegions
1096+
}
1097+
1098+
return transformed, nil
1099+
}
1100+
1101+
func expandBigqueryAnalyticsHubListingPubsubTopicTopic(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1102+
return v, nil
1103+
}
1104+
1105+
func expandBigqueryAnalyticsHubListingPubsubTopicDataAffinityRegions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1106+
v = v.(*schema.Set).List()
1107+
return v, nil
1108+
}
1109+
9821110
func expandBigqueryAnalyticsHubListingRestrictedExportConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
9831111
l := v.([]interface{})
9841112
if len(l) == 0 || l[0] == nil {

google/services/bigqueryanalyticshub/resource_bigquery_analytics_hub_listing_generated_meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ fields:
2525
- field: 'primary_contact'
2626
- field: 'publisher.name'
2727
- field: 'publisher.primary_contact'
28+
- field: 'pubsub_topic.data_affinity_regions'
29+
- field: 'pubsub_topic.topic'
2830
- field: 'request_access'
2931
- field: 'restricted_export_config.enabled'
3032
- field: 'restricted_export_config.restrict_direct_table_access'

google/services/bigqueryanalyticshub/resource_bigquery_analytics_hub_listing_generated_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,62 @@ resource "google_bigquery_dataset" "listing_log_email" {
291291
`, context)
292292
}
293293

294+
func TestAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingPubsubExample(t *testing.T) {
295+
t.Parallel()
296+
297+
context := map[string]interface{}{
298+
"random_suffix": acctest.RandString(t, 10),
299+
}
300+
301+
acctest.VcrTest(t, resource.TestCase{
302+
PreCheck: func() { acctest.AccTestPreCheck(t) },
303+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
304+
CheckDestroy: testAccCheckBigqueryAnalyticsHubListingDestroyProducer(t),
305+
Steps: []resource.TestStep{
306+
{
307+
Config: testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingPubsubExample(context),
308+
},
309+
{
310+
ResourceName: "google_bigquery_analytics_hub_listing.listing",
311+
ImportState: true,
312+
ImportStateVerify: true,
313+
ImportStateVerifyIgnore: []string{"data_exchange_id", "listing_id", "location"},
314+
},
315+
},
316+
})
317+
}
318+
319+
func testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingPubsubExample(context map[string]interface{}) string {
320+
return acctest.Nprintf(`
321+
resource "google_bigquery_analytics_hub_data_exchange" "listing" {
322+
location = "US"
323+
data_exchange_id = "tf_test_tf_test_pubsub_data_exchange%{random_suffix}"
324+
display_name = "tf_test_tf_test_pubsub_data_exchange%{random_suffix}"
325+
description = "Example for pubsub topic source%{random_suffix}"
326+
}
327+
328+
resource "google_pubsub_topic" "tf_test_pubsub_topic" {
329+
name = "tf_test_test_pubsub%{random_suffix}"
330+
}
331+
332+
resource "google_bigquery_analytics_hub_listing" "listing" {
333+
location = "US"
334+
data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing.data_exchange_id
335+
listing_id = "tf_test_tf_test_pubsub_listing%{random_suffix}"
336+
display_name = "tf_test_tf_test_pubsub_listing%{random_suffix}"
337+
description = "Example for pubsub topic source%{random_suffix}"
338+
339+
pubsub_topic {
340+
topic = google_pubsub_topic.tf_test_pubsub_topic.id
341+
data_affinity_regions = [
342+
"us-central1",
343+
"europe-west1"
344+
]
345+
}
346+
}
347+
`, context)
348+
}
349+
294350
func testAccCheckBigqueryAnalyticsHubListingDestroyProducer(t *testing.T) func(s *terraform.State) error {
295351
return func(s *terraform.State) error {
296352
for name, rs := range s.RootModule().Resources {

google/services/bigqueryanalyticshub/resource_bigquery_analytics_hub_listing_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,26 @@ func TestAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingUpdate(t *tes
4646
{
4747
Config: testAccBigqueryAnalyticsHubListing_bigqueryAnalyticshubListingUpdate(context),
4848
},
49+
{
50+
ResourceName: "google_bigquery_analytics_hub_listing.listing",
51+
ImportState: true,
52+
ImportStateVerify: true,
53+
},
54+
{
55+
Config: testAccBigqueryAnalyticsHubListing_pubsubListingUpdateConfig(context, `["us-central1"]`, "Example for pubsub topic source - initial"),
56+
Check: resource.ComposeTestCheckFunc(
57+
// Verify initial state for Pub/Sub listing
58+
resource.TestCheckResourceAttr("google_bigquery_analytics_hub_listing.listing_pubsub", "pubsub_topic.0.data_affinity_regions.#", "1"),
59+
resource.TestCheckResourceAttr("google_bigquery_analytics_hub_listing.listing_pubsub", "pubsub_topic.0.data_affinity_regions.0", "us-central1"),
60+
resource.TestCheckResourceAttr("google_bigquery_analytics_hub_listing.listing_pubsub", "description", "Example for pubsub topic source - initial"),
61+
),
62+
},
63+
// Step 7: Import the updated Pub/Sub Topic listing to verify import after update.
64+
{
65+
ResourceName: "google_bigquery_analytics_hub_listing.listing_pubsub",
66+
ImportState: true,
67+
ImportStateVerify: true,
68+
},
4969
},
5070
})
5171
}
@@ -79,3 +99,45 @@ resource "google_bigquery_dataset" "listing" {
7999
}
80100
`, context)
81101
}
102+
func testAccBigqueryAnalyticsHubListing_pubsubListingUpdateConfig(context map[string]interface{}, dataAffinityRegionsHCL string, description string) string {
103+
// Create a mutable copy of the context map
104+
updatedContext := make(map[string]interface{})
105+
for k, v := range context {
106+
updatedContext[k] = v
107+
}
108+
109+
// Directly assign the HCL string for data_affinity_regions and the description.
110+
// dataAffinityRegionsHCL will be something like `["us-central1"]` or `["us-central1", "europe-west1"]`
111+
updatedContext["data_affinity_regions_hcl"] = dataAffinityRegionsHCL
112+
updatedContext["description_hcl"] = description
113+
114+
return acctest.Nprintf(`
115+
# Separate Data Exchange for the Pub/Sub listing to prevent conflicts
116+
resource "google_bigquery_analytics_hub_data_exchange" "listing_pubsub" {
117+
location = "US"
118+
data_exchange_id = "tf_test_pubsub_data_exchange_update_%{random_suffix}"
119+
display_name = "tf_test_pubsub_data_exchange_update_%{random_suffix}"
120+
description = "Example for pubsub topic source - data exchange%{random_suffix}"
121+
}
122+
123+
# Pub/Sub Topic used as the source for the listing
124+
resource "google_pubsub_topic" "tf_test_pubsub_topic" {
125+
name = "tf_test_test_pubsub_update_%{random_suffix}"
126+
}
127+
128+
# BigQuery Analytics Hub Listing sourced from the Pub/Sub Topic
129+
resource "google_bigquery_analytics_hub_listing" "listing_pubsub" {
130+
location = "US"
131+
data_exchange_id = google_bigquery_analytics_hub_data_exchange.listing_pubsub.data_exchange_id
132+
listing_id = "tf_test_pubsub_listing_update_%{random_suffix}"
133+
display_name = "tf_test_pubsub_listing_update_%{random_suffix}"
134+
description = "%{description_hcl}"
135+
primary_contact = "[email protected]"
136+
137+
pubsub_topic {
138+
topic = google_pubsub_topic.tf_test_pubsub_topic.id
139+
data_affinity_regions = %{data_affinity_regions_hcl}
140+
}
141+
}
142+
`, updatedContext)
143+
}

0 commit comments

Comments
 (0)