Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling management of default Dialogflow resources without terraform import: is_default_x fields #16441

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/9336.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
dialogflowcx: added `is_default_welcome_intent` and `is_default_negative_intent` fields to `google_dialogflow_cx_intent` resource to allow management of default intent resources via Terraform
```
```release-note:enhancement
dialogflowcx: added `is_default_start_flow` field to `google_dialogflow_cx_flow` resource to allow management of default flow resources via Terraform
```
55 changes: 55 additions & 0 deletions google/services/dialogflowcx/resource_dialogflow_cx_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,15 @@ You may set this, for example:
Description: `The unique identifier of the flow.
Format: projects/<Project ID>/locations/<Location ID>/agents/<Agent ID>/flows/<Flow ID>.`,
},
"is_default_start_flow": {
Type: schema.TypeBool,
Optional: true,
ForceNew: 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.

~> Avoid having multiple 'google_dialogflow_cx_flow' resources linked to the same agent with 'is_default_start_flow = true' because they will compete to control a single Default Start Flow resource in GCP.`,
},
},
UseJSONNumber: true,
}
Expand Down Expand Up @@ -776,6 +785,34 @@ func resourceDialogflowCXFlowCreate(d *schema.ResourceData, meta interface{}) er
}

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"
// Note: below we try to access fields that aren't present in the resource, because this custom code is reused across multiple Dialogflow resources that contain different fields. When the field isn't present, we deliberately ignore the error and the boolean is false.
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, "{{parent}}/flows/{{name}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

// and defer to the Update method:
log.Printf("[DEBUG] Updating default DialogflowCXFlow")
return resourceDialogflowCXFlowUpdate(d, meta)
}
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "POST",
Expand Down Expand Up @@ -848,6 +885,8 @@ func resourceDialogflowCXFlowRead(d *schema.ResourceData, meta interface{}) erro
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("DialogflowCXFlow %q", d.Id()))
}

// Explicitly set virtual fields to default values if unset

if err := d.Set("name", flattenDialogflowCXFlowName(res["name"], d, config)); err != nil {
return fmt.Errorf("Error reading Flow: %s", err)
}
Expand Down Expand Up @@ -1047,6 +1086,17 @@ func resourceDialogflowCXFlowDelete(d *schema.ResourceData, meta interface{}) er
}

url = strings.Replace(url, "-dialogflow", fmt.Sprintf("%s-dialogflow", location), 1)

// if it's a default object Dialogflow creates for you, skip deletion
// Note: below we try to access fields that aren't present in the resource, because this custom code is reused across multiple Dialogflow resources that contain different fields. When the field isn't present, we deliberately ignore the error and the boolean is false.
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 DialogflowCXFlow")
return nil
}
log.Printf("[DEBUG] Deleting Flow %q", d.Id())

// err == nil indicates that the billing_project value was found
Expand Down Expand Up @@ -1089,6 +1139,11 @@ func resourceDialogflowCXFlowImport(d *schema.ResourceData, meta interface{}) ([
}
d.SetId(id)

// Set is_default_start_flow if the resource is actually the Default Start Flow
if d.Get("name").(string) == "00000000-0000-0000-0000-000000000000" {
d.Set("is_default_start_flow", true)
}

return []*schema.ResourceData{d}, nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,98 @@ import (
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

func TestAccDialogflowCXFlow_dialogflowcxFlowBasicExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckDialogflowCXFlowDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccDialogflowCXFlow_dialogflowcxFlowBasicExample(context),
},
{
ResourceName: "google_dialogflow_cx_flow.basic_flow",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"parent"},
},
},
})
}

func testAccDialogflowCXFlow_dialogflowcxFlowBasicExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_dialogflow_cx_agent" "agent" {
display_name = "tf-test-dialogflowcx-agent%{random_suffix}"
location = "global"
default_language_code = "en"
supported_language_codes = ["fr", "de", "es"]
time_zone = "America/New_York"
description = "Example description."
avatar_uri = "https://cloud.google.com/_static/images/cloud/icons/favicons/onecloud/super_cloud.png"
enable_stackdriver_logging = true
enable_spell_correction = true
speech_to_text_settings {
enable_speech_adaptation = true
}
}


resource "google_dialogflow_cx_flow" "basic_flow" {
parent = google_dialogflow_cx_agent.agent.id
display_name = "MyFlow"
description = "Test Flow"

nlu_settings {
classification_threshold = 0.3
model_type = "MODEL_TYPE_STANDARD"
}

event_handlers {
event = "custom-event"
trigger_fulfillment {
return_partial_responses = false
messages {
text {
text = ["I didn't get that. Can you say it again?"]
}
}
}
}

event_handlers {
event = "sys.no-match-default"
trigger_fulfillment {
return_partial_responses = false
messages {
text {
text = ["Sorry, could you say that again?"]
}
}
}
}

event_handlers {
event = "sys.no-input-default"
trigger_fulfillment {
return_partial_responses = false
messages {
text {
text = ["One more time?"]
}
}
}
}
}
`, context)
}

func TestAccDialogflowCXFlow_dialogflowcxFlowFullExample(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -342,6 +434,112 @@ resource "google_dialogflow_cx_flow" "basic_flow" {
`, context)
}

func TestAccDialogflowCXFlow_dialogflowcxFlowDefaultStartFlowExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckDialogflowCXFlowDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccDialogflowCXFlow_dialogflowcxFlowDefaultStartFlowExample(context),
},
{
ResourceName: "google_dialogflow_cx_flow.default_start_flow",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"parent"},
},
},
})
}

func testAccDialogflowCXFlow_dialogflowcxFlowDefaultStartFlowExample(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_dialogflow_cx_agent" "agent" {
display_name = "tf-test-dialogflowcx-agent%{random_suffix}"
location = "global"
default_language_code = "en"
time_zone = "America/New_York"
}

resource "google_dialogflow_cx_intent" "default_welcome_intent" {
parent = google_dialogflow_cx_agent.agent.id
is_default_welcome_intent = true
display_name = "Default Welcome Intent"
priority = 1
training_phrases {
parts {
text = "Hello"
}
repeat_count = 1
}
}


resource "google_dialogflow_cx_flow" "default_start_flow" {
parent = google_dialogflow_cx_agent.agent.id
is_default_start_flow = true
display_name = "Default Start Flow"
description = "A start flow created along with the agent"

nlu_settings {
classification_threshold = 0.3
model_type = "MODEL_TYPE_STANDARD"
}

transition_routes {
intent = google_dialogflow_cx_intent.default_welcome_intent.id
trigger_fulfillment {
messages {
text {
text = ["Response to default welcome intent."]
}
}
}
}

event_handlers {
event = "custom-event"
trigger_fulfillment {
messages {
text {
text = ["This is a default flow."]
}
}
}
}

event_handlers {
event = "sys.no-match-default"
trigger_fulfillment {
messages {
text {
text = ["We've updated the default flow no-match response!"]
}
}
}
}

event_handlers {
event = "sys.no-input-default"
trigger_fulfillment {
messages {
text {
text = ["We've updated the default flow no-input response!"]
}
}
}
}
}
`, context)
}

func testAccCheckDialogflowCXFlowDestroyProducer(t *testing.T) func(s *terraform.State) error {
return func(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
Expand Down
Loading