From ed9503c11c16cc8d38f61a9cd5ce5157410fe5ad Mon Sep 17 00:00:00 2001 From: Maciej Murakowski Date: Mon, 23 Oct 2023 16:33:03 -0400 Subject: [PATCH] Enabling management of Default Start Flow + Default Welcome Intent + Default Negative Intent, without terraform import Fixes https://github.com/hashicorp/terraform-provider-google/issues/16308 --- mmv1/products/dialogflowcx/Flow.yaml | 11 ++++- mmv1/products/dialogflowcx/Intent.yaml | 19 ++++++++- mmv1/provider/terraform/virtual_fields.rb | 5 +++ ...lowcx_set_location_skip_default_obj.go.erb | 41 +++++++++++++++++++ ...lowcx_set_location_skip_default_obj.go.erb | 25 +++++++++++ mmv1/templates/terraform/resource.erb | 3 ++ 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 mmv1/templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb create mode 100644 mmv1/templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb diff --git a/mmv1/products/dialogflowcx/Flow.yaml b/mmv1/products/dialogflowcx/Flow.yaml index a524b799f8a6..5e014e659328 100644 --- a/mmv1/products/dialogflowcx/Flow.yaml +++ b/mmv1/products/dialogflowcx/Flow.yaml @@ -27,10 +27,17 @@ timeouts: !ruby/object:Api::Timeouts update_minutes: 40 custom_code: !ruby/object:Provider::Terraform::CustomCode custom_import: templates/terraform/custom_import/dialogflowcx_flow.go.erb - pre_create: templates/terraform/pre_create/dialogflow_set_location.go.erb + pre_create: templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb pre_update: templates/terraform/pre_create/dialogflow_set_location.go.erb - pre_delete: templates/terraform/pre_create/dialogflow_set_location.go.erb + pre_delete: templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb pre_read: templates/terraform/pre_create/dialogflow_set_location.go.erb +virtual_fields: + - !ruby/object:Api::Type::Boolean + name: is_default_start_flow + immutable: true + description: | + Marks this as the [Default Start Flow](https://cloud.google.com/dialogflow/cx/docs/concept/flow#start) for an agent. When you create an agent, the Default Start Flow is created automatically. + The Default Start Flow cannot be deleted; deleting the `google_dialogflow_cx_flow` resource does nothing to the underlying GCP resources. examples: - !ruby/object:Provider::Terraform::Examples name: 'dialogflowcx_flow_full' diff --git a/mmv1/products/dialogflowcx/Intent.yaml b/mmv1/products/dialogflowcx/Intent.yaml index 479e7756756e..9555ed6b4122 100644 --- a/mmv1/products/dialogflowcx/Intent.yaml +++ b/mmv1/products/dialogflowcx/Intent.yaml @@ -27,10 +27,23 @@ timeouts: !ruby/object:Api::Timeouts update_minutes: 40 custom_code: !ruby/object:Provider::Terraform::CustomCode custom_import: templates/terraform/custom_import/dialogflowcx_intent.go.erb - pre_create: templates/terraform/pre_create/dialogflow_set_location.go.erb + pre_create: templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb pre_update: templates/terraform/pre_create/dialogflow_set_location.go.erb - pre_delete: templates/terraform/pre_create/dialogflow_set_location.go.erb + pre_delete: templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb pre_read: templates/terraform/pre_create/dialogflow_set_location.go.erb +virtual_fields: + - !ruby/object:Api::Type::Boolean + name: is_default_welcome_intent + immutable: true + description: | + Marks this as the [Default Welcome Intent](https://cloud.google.com/dialogflow/cx/docs/concept/intent#welcome) for an agent. When you create an agent, a Default Welcome Intent is created automatically. + The Default Welcome Intent cannot be deleted; deleting the `google_dialogflow_cx_intent` resource does nothing to the underlying GCP resources. + - !ruby/object:Api::Type::Boolean + name: is_default_negative_intent + immutable: true + description: | + Marks this as the [Default Negative Intent](https://cloud.google.com/dialogflow/cx/docs/concept/intent#negative) for an agent. When you create an agent, a Default Negative Intent is created automatically. + The Default Negative Intent cannot be deleted; deleting the `google_dialogflow_cx_intent` resource does nothing to the underlying GCP resources. examples: - !ruby/object:Provider::Terraform::Examples name: 'dialogflowcx_intent_full' @@ -141,9 +154,11 @@ properties: If the supplied value is negative, the intent is ignored in runtime detect intent requests. - !ruby/object:Api::Type::Boolean name: 'isFallback' + output: true description: | Indicates whether this is a fallback intent. Currently only default fallback intent is allowed in the agent, which is added upon agent creation. Adding training phrases to fallback intent is useful in the case of requests that are mistakenly matched, since training phrases assigned to fallback intents act as negative examples that triggers no-match event. + To manage the fallback intent, set `is_default_negative_intent = true` - !ruby/object:Api::Type::KeyValueLabels name: 'labels' description: | diff --git a/mmv1/provider/terraform/virtual_fields.rb b/mmv1/provider/terraform/virtual_fields.rb index e81bdbffe52e..66acd30b3e03 100644 --- a/mmv1/provider/terraform/virtual_fields.rb +++ b/mmv1/provider/terraform/virtual_fields.rb @@ -49,12 +49,17 @@ class VirtualFields < Api::Object # The default value for the field (defaults to false) attr_reader :default_value + # If set to true, changes in the field's value require recreating the + # resource. + attr_reader :immutable + def validate super check :name, type: String, required: true check :description, type: String, required: true check :type, type: Class, default: Api::Type::Boolean check :default_value, default: false + check :immutable, default: false end end end diff --git a/mmv1/templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb b/mmv1/templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb new file mode 100644 index 000000000000..8f42c34a82f2 --- /dev/null +++ b/mmv1/templates/terraform/pre_create/dialogflowcx_set_location_skip_default_obj.go.erb @@ -0,0 +1,41 @@ + +// extract location from the parent +location := "" + +if parts := regexp.MustCompile(`locations\/([^\/]*)\/`).FindStringSubmatch(d.Get("parent").(string)); parts != nil { + location = parts[1] +} else { + return fmt.Errorf( + "Saw %s when the parent is expected to contains location %s", + d.Get("parent"), + "projects/{{project}}/locations/{{location}}/...", + ) +} + +url = strings.Replace(url,"-dialogflow",fmt.Sprintf("%s-dialogflow",location),1) + +// if it's a default object Dialogflow creates for you, "Update" instead of "Create" +isDefaultStartFlow, _ := d.Get("is_default_start_flow").(bool) +isDefaultWelcomeIntent, _ := d.Get("is_default_welcome_intent").(bool) +isDefaultNegativeIntent, _ := d.Get("is_default_negative_intent").(bool) +if isDefaultStartFlow || isDefaultWelcomeIntent || isDefaultNegativeIntent { + // hardcode the default object ID: + var defaultObjName string + if isDefaultStartFlow || isDefaultWelcomeIntent { + defaultObjName = "00000000-0000-0000-0000-000000000000" + } + if isDefaultNegativeIntent { + defaultObjName = "00000000-0000-0000-0000-000000000001" + } + + // Store the ID + d.Set("name", defaultObjName) + id, err := tpgresource.ReplaceVars(d, config, "<%= id_format(object) -%>") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // and defer to the Update method: + return resource<%= resource_name -%>Update(d, meta) +} \ No newline at end of file diff --git a/mmv1/templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb b/mmv1/templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb new file mode 100644 index 000000000000..1a861060a573 --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/dialogflowcx_set_location_skip_default_obj.go.erb @@ -0,0 +1,25 @@ + +// extract location from the parent +location := "" + +if parts := regexp.MustCompile(`locations\/([^\/]*)\/`).FindStringSubmatch(d.Get("parent").(string)); parts != nil { + location = parts[1] +} else { + return fmt.Errorf( + "Saw %s when the parent is expected to contains location %s", + d.Get("parent"), + "projects/{{project}}/locations/{{location}}/...", + ) +} + +url = strings.Replace(url,"-dialogflow",fmt.Sprintf("%s-dialogflow",location),1) + +// if it's a default object Dialogflow creates for you, skip deletion +isDefaultStartFlow, _ := d.Get("is_default_start_flow").(bool) +isDefaultWelcomeIntent, _ := d.Get("is_default_welcome_intent").(bool) +isDefaultNegativeIntent, _ := d.Get("is_default_negative_intent").(bool) +if isDefaultStartFlow || isDefaultWelcomeIntent || isDefaultNegativeIntent { + // we can't delete these resources so do nothing + log.Printf("[DEBUG] Not deleting default <%= resource_name -%>") + return nil +} \ No newline at end of file diff --git a/mmv1/templates/terraform/resource.erb b/mmv1/templates/terraform/resource.erb index bdc6c65af96e..edeaa9a62f1c 100644 --- a/mmv1/templates/terraform/resource.erb +++ b/mmv1/templates/terraform/resource.erb @@ -155,6 +155,9 @@ func Resource<%= resource_name -%>() *schema.Resource { "<%= field.name -%>": { Type: <%= tf_type(field) -%>, Optional: true, +<% if field.immutable -%> + ForceNew: true, +<% end -%> <% unless field.default_value.nil? -%> Default: <%= go_literal(field.default_value) -%>, <% end -%>