diff --git a/.changelog/3417.txt b/.changelog/3417.txt new file mode 100644 index 00000000000..051a6892877 --- /dev/null +++ b/.changelog/3417.txt @@ -0,0 +1,48 @@ +```release-note:new-resource +`google_healthcare_dataset` is now GA +``` +```release-note:new-resource +`google_healthcare_dicom_store` is now GA +``` +```release-note:new-resource +`google_healthcare_fhir_store` is now GA +``` +```release-note:new-resource +`google_healthcare_hl7_v2_store` is now GA +``` +```release-note:new-resource +`google_healthcare_dataset_iam_binding` is now GA +``` +```release-note:new-resource +`google_healthcare_dataset_iam_member` is now GA +``` +```release-note:new-resource +`google_healthcare_dataset_iam_policy` is now GA +``` +```release-note:new-resource +`google_healthcare_dicom_store_iam_binding` is now GA +``` +```release-note:new-resource +`google_healthcare_dicom_store_iam_member` is now GA +``` +```release-note:new-resource +`google_healthcare_dicom_store_iam_policy` is now GA +``` +```release-note:new-resource +`google_healthcare_fhir_store_iam_binding` is now GA +``` +```release-note:new-resource +`google_healthcare_fhir_store_iam_member` is now GA +``` +```release-note:new-resource +`google_healthcare_fhir_store_iam_policy` is now GA +``` +```release-note:new-resource +`google_healthcare_hl7_v2_store_iam_binding` is now GA +``` +```release-note:new-resource +`google_healthcare_hl7_v2_store_iam_member` is now GA +``` +```release-note:new-resource +`google_healthcare_hl7_v2_store_iam_policy` is now GA +``` diff --git a/google/config.go b/google/config.go index 40a5d72eaf7..8ce990086e6 100644 --- a/google/config.go +++ b/google/config.go @@ -38,7 +38,7 @@ import ( "google.golang.org/api/dns/v1" dnsBeta "google.golang.org/api/dns/v1beta2" file "google.golang.org/api/file/v1beta1" - healthcare "google.golang.org/api/healthcare/v1beta1" + healthcare "google.golang.org/api/healthcare/v1" "google.golang.org/api/iam/v1" iamcredentials "google.golang.org/api/iamcredentials/v1" cloudlogging "google.golang.org/api/logging/v2" diff --git a/google/iam_healthcare_dataset.go b/google/iam_healthcare_dataset.go index 2f0b9fb621a..5179fff850a 100644 --- a/google/iam_healthcare_dataset.go +++ b/google/iam_healthcare_dataset.go @@ -3,7 +3,7 @@ package google import ( "fmt" - healthcare "google.golang.org/api/healthcare/v1beta1" + healthcare "google.golang.org/api/healthcare/v1" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" diff --git a/google/iam_healthcare_dicom_store.go b/google/iam_healthcare_dicom_store.go index c3c44cd9fcf..ff7d946b1bc 100644 --- a/google/iam_healthcare_dicom_store.go +++ b/google/iam_healthcare_dicom_store.go @@ -3,7 +3,7 @@ package google import ( "fmt" - healthcare "google.golang.org/api/healthcare/v1beta1" + healthcare "google.golang.org/api/healthcare/v1" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" diff --git a/google/iam_healthcare_fhir_store.go b/google/iam_healthcare_fhir_store.go index 6d1663b209a..ebb513e5103 100644 --- a/google/iam_healthcare_fhir_store.go +++ b/google/iam_healthcare_fhir_store.go @@ -3,7 +3,7 @@ package google import ( "fmt" - healthcare "google.golang.org/api/healthcare/v1beta1" + healthcare "google.golang.org/api/healthcare/v1" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" diff --git a/google/iam_healthcare_hl7_v2_store.go b/google/iam_healthcare_hl7_v2_store.go index f26022a80d2..77f7561df5c 100644 --- a/google/iam_healthcare_hl7_v2_store.go +++ b/google/iam_healthcare_hl7_v2_store.go @@ -3,7 +3,7 @@ package google import ( "fmt" - healthcare "google.golang.org/api/healthcare/v1beta1" + healthcare "google.golang.org/api/healthcare/v1" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" diff --git a/google/resource_healthcare_hl7_v2_store.go b/google/resource_healthcare_hl7_v2_store.go index bb1b98d9968..60e440054e5 100644 --- a/google/resource_healthcare_hl7_v2_store.go +++ b/google/resource_healthcare_hl7_v2_store.go @@ -81,6 +81,7 @@ Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.`, "notification_config": { Type: schema.TypeList, Optional: true, + Removed: "This field has been replaced by notificationConfigs", Description: `A nested object resource`, MaxItems: 1, Elem: &schema.Resource{ @@ -98,6 +99,44 @@ Cloud Pub/Sub topic. Not having adequate permissions will cause the calls that s }, }, }, + "notification_configs": { + Type: schema.TypeList, + Optional: true, + Description: `A list of notification configs. Each configuration uses a filter to determine whether to publish a +message (both Ingest & Create) on the corresponding notification destination. Only the message name +is sent as part of the notification. Supplied by the client.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pubsub_topic": { + Type: schema.TypeString, + Required: true, + Description: `The Cloud Pub/Sub topic that notifications of changes are published on. Supplied by the client. +PubsubMessage.Data will contain the resource name. PubsubMessage.MessageId is the ID of this message. +It is guaranteed to be unique within the topic. PubsubMessage.PublishTime is the time at which the message +was published. Notifications are only sent if the topic is non-empty. Topic names must be scoped to a +project. cloud-healthcare@system.gserviceaccount.com must have publisher permissions on the given +Cloud Pub/Sub topic. Not having adequate permissions will cause the calls that send notifications to fail. + +If a notification cannot be published to Cloud Pub/Sub, errors will be logged to Stackdriver`, + }, + "filter": { + Type: schema.TypeString, + Optional: true, + Description: `Restricts notifications sent for messages matching a filter. If this is empty, all messages +are matched. Syntax: https://cloud.google.com/appengine/docs/standard/python/search/query_strings + +Fields/functions available for filtering are: + +* messageType, from the MSH-9.1 field. For example, NOT messageType = "ADT". +* send_date or sendDate, the YYYY-MM-DD date the message was sent in the dataset's timeZone, from the MSH-7 segment. For example, send_date < "2017-01-02". +* sendTime, the timestamp when the message was sent, using the RFC3339 time format for comparisons, from the MSH-7 segment. For example, sendTime < "2017-01-02T00:00:00-05:00". +* sendFacility, the care center that the message came from, from the MSH-4 segment. For example, sendFacility = "ABC". +* PatientId(value, type), which matches if the message lists a patient having an ID of the given value and type in the PID-2, PID-3, or PID-4 segments. For example, PatientId("123456", "MRN"). +* labels.x, a string value of the label with key x as set using the Message.labels map. For example, labels."priority"="high". The operator :* can be used to assert the existence of a label. For example, labels."priority":*.`, + }, + }, + }, + }, "parser_config": { Type: schema.TypeList, Optional: true, @@ -162,6 +201,12 @@ func resourceHealthcareHl7V2StoreCreate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { obj["labels"] = labelsProp } + notificationConfigsProp, err := expandHealthcareHl7V2StoreNotificationConfigs(d.Get("notification_configs"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("notification_configs"); !isEmptyValue(reflect.ValueOf(notificationConfigsProp)) && (ok || !reflect.DeepEqual(v, notificationConfigsProp)) { + obj["notificationConfigs"] = notificationConfigsProp + } notificationConfigProp, err := expandHealthcareHl7V2StoreNotificationConfig(d.Get("notification_config"), d, config) if err != nil { return err @@ -226,6 +271,9 @@ func resourceHealthcareHl7V2StoreRead(d *schema.ResourceData, meta interface{}) if err := d.Set("labels", flattenHealthcareHl7V2StoreLabels(res["labels"], d, config)); err != nil { return fmt.Errorf("Error reading Hl7V2Store: %s", err) } + if err := d.Set("notification_configs", flattenHealthcareHl7V2StoreNotificationConfigs(res["notificationConfigs"], d, config)); err != nil { + return fmt.Errorf("Error reading Hl7V2Store: %s", err) + } if err := d.Set("notification_config", flattenHealthcareHl7V2StoreNotificationConfig(res["notificationConfig"], d, config)); err != nil { return fmt.Errorf("Error reading Hl7V2Store: %s", err) } @@ -249,6 +297,12 @@ func resourceHealthcareHl7V2StoreUpdate(d *schema.ResourceData, meta interface{} } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { obj["labels"] = labelsProp } + notificationConfigsProp, err := expandHealthcareHl7V2StoreNotificationConfigs(d.Get("notification_configs"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("notification_configs"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, notificationConfigsProp)) { + obj["notificationConfigs"] = notificationConfigsProp + } notificationConfigProp, err := expandHealthcareHl7V2StoreNotificationConfig(d.Get("notification_config"), d, config) if err != nil { return err @@ -272,6 +326,10 @@ func resourceHealthcareHl7V2StoreUpdate(d *schema.ResourceData, meta interface{} updateMask = append(updateMask, "labels") } + if d.HasChange("notification_configs") { + updateMask = append(updateMask, "notificationConfigs") + } + if d.HasChange("notification_config") { updateMask = append(updateMask, "notificationConfig") } @@ -370,6 +428,33 @@ func flattenHealthcareHl7V2StoreLabels(v interface{}, d *schema.ResourceData, co return v } +func flattenHealthcareHl7V2StoreNotificationConfigs(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "pubsub_topic": flattenHealthcareHl7V2StoreNotificationConfigsPubsubTopic(original["pubsubTopic"], d, config), + "filter": flattenHealthcareHl7V2StoreNotificationConfigsFilter(original["filter"], d, config), + }) + } + return transformed +} +func flattenHealthcareHl7V2StoreNotificationConfigsPubsubTopic(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenHealthcareHl7V2StoreNotificationConfigsFilter(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenHealthcareHl7V2StoreNotificationConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil @@ -455,6 +540,43 @@ func expandHealthcareHl7V2StoreLabels(v interface{}, d TerraformResourceData, co return m, nil } +func expandHealthcareHl7V2StoreNotificationConfigs(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedPubsubTopic, err := expandHealthcareHl7V2StoreNotificationConfigsPubsubTopic(original["pubsub_topic"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedPubsubTopic); val.IsValid() && !isEmptyValue(val) { + transformed["pubsubTopic"] = transformedPubsubTopic + } + + transformedFilter, err := expandHealthcareHl7V2StoreNotificationConfigsFilter(original["filter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFilter); val.IsValid() && !isEmptyValue(val) { + transformed["filter"] = transformedFilter + } + + req = append(req, transformed) + } + return req, nil +} + +func expandHealthcareHl7V2StoreNotificationConfigsPubsubTopic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandHealthcareHl7V2StoreNotificationConfigsFilter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandHealthcareHl7V2StoreNotificationConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { diff --git a/google/resource_healthcare_hl7_v2_store_generated_test.go b/google/resource_healthcare_hl7_v2_store_generated_test.go index 83465275ce5..35040ae9c24 100644 --- a/google/resource_healthcare_hl7_v2_store_generated_test.go +++ b/google/resource_healthcare_hl7_v2_store_generated_test.go @@ -54,7 +54,7 @@ resource "google_healthcare_hl7_v2_store" "default" { name = "tf-test-example-hl7-v2-store%{random_suffix}" dataset = google_healthcare_dataset.dataset.id - notification_config { + notification_configs { pubsub_topic = google_pubsub_topic.topic.id } diff --git a/google/resource_healthcare_hl7_v2_store_test.go b/google/resource_healthcare_hl7_v2_store_test.go index afd1fd87441..7184b4c1c09 100644 --- a/google/resource_healthcare_hl7_v2_store_test.go +++ b/google/resource_healthcare_hl7_v2_store_test.go @@ -138,7 +138,7 @@ resource "google_healthcare_hl7_v2_store" "default" { segment_terminator = "Jw==" } - notification_config { + notification_configs { pubsub_topic = google_pubsub_topic.topic.id } @@ -195,9 +195,12 @@ func testAccCheckGoogleHealthcareHl7V2StoreUpdate(t *testing.T, pubsubTopic stri return fmt.Errorf("hl7_v2_store labels not updated: %s", gcpResourceUri) } - topicName := path.Base(response.NotificationConfig.PubsubTopic) - if topicName != pubsubTopic { - return fmt.Errorf("hl7_v2_store 'NotificationConfig' not updated ('%s' != '%s'): %s", topicName, pubsubTopic, gcpResourceUri) + notifications := response.NotificationConfigs + if len(notifications) > 0 { + topicName := path.Base(notifications[0].PubsubTopic) + if topicName != pubsubTopic { + return fmt.Errorf("hl7_v2_store 'NotificationConfig' not updated ('%s' != '%s'): %s", topicName, pubsubTopic, gcpResourceUri) + } } } diff --git a/website/docs/r/healthcare_hl7_v2_store.html.markdown b/website/docs/r/healthcare_hl7_v2_store.html.markdown index 85f16150e64..ad08df7d4a1 100644 --- a/website/docs/r/healthcare_hl7_v2_store.html.markdown +++ b/website/docs/r/healthcare_hl7_v2_store.html.markdown @@ -45,7 +45,7 @@ resource "google_healthcare_hl7_v2_store" "default" { name = "example-hl7-v2-store" dataset = google_healthcare_dataset.dataset.id - notification_config { + notification_configs { pubsub_topic = google_pubsub_topic.topic.id } @@ -204,9 +204,11 @@ The following arguments are supported: An object containing a list of "key": value pairs. Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }. -* `notification_config` - +* `notification_configs` - (Optional) - A nested object resource Structure is documented below. + A list of notification configs. Each configuration uses a filter to determine whether to publish a + message (both Ingest & Create) on the corresponding notification destination. Only the message name + is sent as part of the notification. Supplied by the client. Structure is documented below. The `parser_config` block supports: @@ -225,7 +227,7 @@ The `parser_config` block supports: JSON encoded string for schemas used to parse messages in this store if schematized parsing is desired. -The `notification_config` block supports: +The `notification_configs` block supports: * `pubsub_topic` - (Required) @@ -235,6 +237,19 @@ The `notification_config` block supports: was published. Notifications are only sent if the topic is non-empty. Topic names must be scoped to a project. cloud-healthcare@system.gserviceaccount.com must have publisher permissions on the given Cloud Pub/Sub topic. Not having adequate permissions will cause the calls that send notifications to fail. + If a notification cannot be published to Cloud Pub/Sub, errors will be logged to Stackdriver + +* `filter` - + (Optional) + Restricts notifications sent for messages matching a filter. If this is empty, all messages + are matched. Syntax: https://cloud.google.com/appengine/docs/standard/python/search/query_strings + Fields/functions available for filtering are: + * messageType, from the MSH-9.1 field. For example, NOT messageType = "ADT". + * send_date or sendDate, the YYYY-MM-DD date the message was sent in the dataset's timeZone, from the MSH-7 segment. For example, send_date < "2017-01-02". + * sendTime, the timestamp when the message was sent, using the RFC3339 time format for comparisons, from the MSH-7 segment. For example, sendTime < "2017-01-02T00:00:00-05:00". + * sendFacility, the care center that the message came from, from the MSH-4 segment. For example, sendFacility = "ABC". + * PatientId(value, type), which matches if the message lists a patient having an ID of the given value and type in the PID-2, PID-3, or PID-4 segments. For example, PatientId("123456", "MRN"). + * labels.x, a string value of the label with key x as set using the Message.labels map. For example, labels."priority"="high". The operator :* can be used to assert the existence of a label. For example, labels."priority":*. ## Attributes Reference